
I got an email from a friend of mine who is building a BBS app that he wants to have threaded comments. Basically:
- This is the top post
- This is the reply to the top
- This is the reply to the reply
He’s using classic ASP, and was looking for a way to avoid having to do a recursive query to get the information. At first I was thinking of some SQL strategies to help avoid it but then thought that XML might be a good answer to it.
Here is some cool BBS type stuff:
Output:
The other day I get to work and realize I left a paper at the house that I need to have that morning. I can’t just print out a new one because it’s a document that can’t just be printed (like a check). Scanning it in is out of the question because the scanner we have is on my primary machine – a Linux box – with XSane set up on it for scanning, and Gimp for image editing. My wife is fairly computer literate, but on Windows. So I guess I’m out of luck. Or am I?
I have my wife put the document on my scanner and proceed to Googling. My first stop was XSane which is a graphical interface to the SANE libraries. From their site:
SANE stands for “Scanner Access Now Easy” and is an application programming interface (API) that provides standardized access to any raster image scanner hardware (flatbed scanner, hand-held scanner, video- and still-cameras, frame-grabbers, etc.).
Since XSane is a GUI to SANE, and knowing that Linux just isn’t Linux unless you can do everything from a command line, I peek at the SANE docs. Sure enough, there it is:
scanimage is a command-line interface to control image acquisition devices such as flatbed scanners or cameras. The device is controlled via command-line options. After command-line processing, scanimage normally proceeds to acquire an image. The image data is written to standard output in one of the PNM (portable aNyMaP) formats (PBM for black-and-white images, PGM for grayscale images, and PPM for color images) or in TIFF (black-and-white, grayscale or color). scanimage accesses image acquisition devices through the SANE (Scanner Access Now Easy) interface and can thus support any device for which there exists a SANE backend (try apropos sane- to get a list of available backends).
So I fire up SSH to my box, type in the command and immediately get an IM from my wife saying that my scanner has got a life of its own. After some tweaking with the settings, I was able to get it to scan the full image in. I then just put it on my FTP server and grabbed it normally from there.
Ahh the power of the internet. I would have never in a million years thought that a command-line interface to a scanner would come in handy, but I humbly admin that I was wrong.
The current project I’m working on interfaces with a third-party screen-scraping robot. Actually, lots of them, it could be close to a thousand by the time all is said and done. The challenge was to create some sort of usable interface between the value structures the robot returns and the actual business objects that make up my app. In addition, the naming of those value structures don’t match my business objects.
For example, if I was using a robot for a banking site, it might take in four parameters, say “user_name”, “password”, “account_number” and “account_type” and return three parameters, “last_login_date”, “current_balance” and “site_messages.” Of course, last_login_date and current_balance would be strings, not date and double objects because of the way the robots are. Also, because of the size of the app, and because I only want the robot to get the information and not have to worry about anything else with it, I wanted a way to convert the values to a usable object, say, a DataSet.
In my first stab at it, I created an object mapper that used reflection to create a DataTable for each unique object type returned by the object, with each table containing a row for each object of that type returned. I then created an object mapper for each type of robot that would take the DataSet and convert it to the object type I needed.
So, extending the example from above, I might have two bank robots, one for Example Bank and one for Joe’s Bank. One robot might look for an ExampleBankInput object and return ExampleBankOutput objects, while the other would look for JoesBankInput object and return JoesBankOutput objects. However, these input and output objects didn’t necessarily relate to the business objects I wanted to use in my app. I want user_name to be UserName and password to be Password and both of them to be in a User object so that I don’t end up with 7,000 different objects that all do basically the same thing. But then I want last_login_date to be LastLoginDate and be a DateTime field that is in an object specific to the bank I am working with because each bank might implement it differently.
So to make this work, I created more object mappers, one for each special type that I had. It only took me creating about three of these to realize there had to be a better way to do it. How can I create a generic mapping class that knows how to convert all of my business objects back and forth, without me having to write complex custom logic?
The answer? Method Attributes.
If you haven’t seen them before, c# allows you to attach attributes to methods:
[MyCustomAttribute("blah")]
public void MyMethod()
{
//some stuff here
}
In the above example, MyCustomAttribute would be a class that had a constructor that took a single parameter of a string. How does that help? Well, because in c# Properties are also methods, you can apply custom attributes to properties. Hence a customizable value object.
public class BankUser
{
private string userName;
private string password;
public BankUser(string userName, string password)
{
this.userName = userName;
this.password = password;
}
[BankRobotInputAttribute("user_name")]
[BankRobotOutputAttribute("user_name")]
public string UserName
{
get{return userName;}
set{userName = value;}
}
[BankRobotInputAttribute("password")]
[BankRobotOutputAttribute("password")]
public string Password
{
get{return password;}
set{password = value;}
}
}
So then, all I have to do is write a mapper that takes in an object, reads through it’s properties to get custom attributes for the direction the value is heading, and get the value from the property. This is a piece of cake for string values, but what about other types, say DateTime and double like I mentioned earlier? The workaround I used was to create a property just for handling string representations of these values:
public class BankAccount
{
private DateTime lastLoginDate;
private double accountBalance;
public BankAccount() {}
public DateTime LastLoginDate
{
get{return lastLoginDate;}
set{lastLoginDate = value;}
}
public double AccountBalance
{
get{return accountBalance;}
set{accountBalance = value;}
}
[BankRobotOutputAttribute("last_login_date")]
public string s_LastLoginDate
{
set
{
if(value != null && value != String.Empty)
{
try
{
lastLoginDate=DateTime.Parse(value);
}
catch {}
}
}
}
[BankRobotOutputAttribute("account_balance")]
public string s_AccountBalance
{
set
{
if(value != null && value != String.Empty)
{
try
{
accountBalance = double.Parse(value);
}
catch {}
}
}
}
}
This lets me create a mapper like
public static Object FillObject(Object obj, DataSet ds, String tableName)
{
DataTable table = ds.Tables[tableName];
DataRow row = table.Rows[0];
foreach(PropertyInfo prop in obj.GetType().GetProperties())
{
object[] customAttributes =
prop.GetCustomAttributes(typeof(BankRobotOutputAttribute),true);
if(customAttributes.Length > 0)
{
BankRobotOutputAttribute a =(BankRobotOutputAttribute)customAttributes[0];
if(table.Columns.Contains(a.FieldName))
{
DataColumn col = table.Columns[a.FieldName];
prop.SetValue(obj,row[col], null);
}
}
}
return obj;
}