I think it was Steve McConnell that first made me aware of the chicken roasting metaphor. In the story, Great Grandma McConnell (presumably) lived in a fairly small house but managed a close knit family circle. Each Sunday the family would come around and she'd roast them up a chicken, with all the trimmings of course, and serve a sumptuous family lunch. It became tradition after a short while though that the women of the family would help her prepare the meal (we're talking a long time ago here, obviously before the male brain evolved to the point that cooking was a skill they could acquire).
Now, Grandma's oven was small, but with a large family she needed to roast a large chicken each week. More often than not, Grandma would find that the chicken would not fit in the oven on a single shelf, so she would teach the onlookers that part of her solution to the problem was to remove the chicken's legs and bake them in a separate tray. Over the years this became the "standard" way to do things in that family. To cook the chicken, you'd baste it, stuff it, remove the legs and put two trays in the oven. When she passed away and the next generation took over, the tradition continued, and so on, to the present day. When asked in recent years but a younger member of the family "Grandma, why don't we just put the whole chicken in our Mondo-Fan-Assisted-Roastamatic 3000", the current 'Grandma' sneered, patted junior on the head and said "Because this is how we've always done it.". Despite the fact that the modern ovens are large enough to take the whole chicken, the family tradition of cutting off the legs first and cooking them separately continued. It's a pretty inefficient use of the oven space in some ways, and certainly a task that's no longer necessary, but since that is how it's always been done, that is how it will continue to be done.
When I first came across the world of XML, a confusing world full of academics and tweed wearing people at the time, I was embroiled in my first Enterprise Java project. I learned about the two methods of working with XML (SAX and the DOM) one of which gave me a fast forward reading stream into the XML document, and one of which represented the entire XML tree in memory for me to navigate around. I spent a long time learning the various parsers and standards out there in order to read my XML documents, and from them populate objects in memory, and all was good, if a little slow, tedious and bug-prone. When JDOM came along I rejoiced in it's simplicity (for those of you that don't know JDOM, it's a much less "academic" way to browse an XML source) and adopted it with some gusto, shortening my code bases and generally having a whale of a time.
When I came across the XML toolkit for VB, and then later the .NET class library's support for XML, I applied what I knew to those new toolsets. I would work the same way though, creating my shell objects, opening the XML source, reading it node by node, and populating the properties of my shells until eventually I had all the data I needed in a handy object based form. I took the chicken roasting approach to XML development. "This is how I've always done it, I know it works, and so this is how I'll carry on doing it". It's amazing to me now that I ever worked that way, and more amazing still to see others doing the same.
The issue is that most people read an XML document to extract information which at some point gets put into fields and properties in objects. So, many of us write code - vast, lengthy tracts of unweildy code - to do just that. But, in .NET XML is an object. With .NET your XML documents really can be thought of a serialized objects. Everybody already talks about how cool it is that we can serialize to XML, but just how many people out there really do it? I've not seen many, and I've had the pleasure of working with some of the very best people in the industry. .NET's XMLSerializer will both read and write XML into objects. When writing, public fields and properties (including object's and arrays) get written out to the XML file as Elements. When reading, Elements populate identically named fields and properties of a specified class. However, you can change this default behaviour to allow pretty much any format of XML document to hydrate an object.
Take a look at this XML <Customer> <Name>Harry Webb</Name> <Company>Webbs Wonder Widgets</Company> </Customer>
This can easily be used to hyrdate a customer object. Take a look at the following code to see how using System; using System.IO; using System.Xml.Serialization; namespace XMLSerialize2 { class Class1 { [STAThread] static void Main(string[] args) { FileStream stream = new FileStream("c:\\customer.xml", FileMode.Open ); XmlSerializer ser = new XmlSerializer( typeof (Customer) ); Customer myCustomer = (Customer) ser.Deserialize( stream ); } } public class Customer { public string Name; public string Company; } }
Trivial isn't it. All the code does is open a stream to read in the XML, and then create an XMLSerializer passing in the type of object that we are going to want to work with. Finally, the object is created by asking the serializer to Deserialize, passing in the stream. You can reverse the process and write out to a file with a call to Serialize, passing in the customer object itself as the second argument.
What about if the XML get's more complex though. Take a look at this
<Customer ID="12331">
<Name>Harry Webb</Name>
<Company>Webbs Wonder Widgets</Company>
<Orders>
<Order Number="12332" PO="123123">
<Product>The widget Mark 1</Product>
<Price>123.22</Price>
<Quantity>54234</Quantity>
</Order>
<Order Number="222" PO="1123">
<Product>The widget Mark 2</Product>
<Price>13.22</Price>
<Quantity>4234</Quantity>
</Order>
<Order Number="444123" PO="1aww3">
<Product>The widget Mark 3</Product>
<Price>23.22</Price>
<Quantity>534</Quantity>
</Order>
</Orders>
</Customer>
That's a little more complex isn't it. By attaching attributes to the destination classes though, reading this into an object hierarchy is still trivial public class Customer { [XmlAttribute] public int ID; public string Name; public string Company; [XmlArray("Orders")] public Order[] CustomerOrders; } public class Order { [XmlAttribute("Number")] public int OrderNumber; [XmlAttribute("PO")] public string PurchaseOrderNumber; public string Product; public double Price; public int Quantity; }
Where attributes are used in the original XML document, prefxing the field in the class with [XmlAttribute] tells the serializer to look for an attribute with the same name. You'll also notice that in some of the attributes I've actually specified the name of the original element in the XML document where I have used a different field name in the class. Arrays are also catered for with the [XmlArray] attribute, and again I've specified the actual parent element name in the original XML document.
Easy isn't it. I'm never going back to using System.XML after this revelation. Thanks to Keith Ballinger's excellent book Essential Web Services for the lead. There's a lot more information in that book on the other things you can do to radically change the structure of the written document, as well as how to read SOAP Encoded documents into classes. Fantastic stuff.
5:50:23 PM
|