Fun with Anonymous Types and LINQ to XML

[Blog Map]  This blog is inactive.  New blog: EricWhite.com/blog

(August 2, 2008: This post uses the wrong approach.  I've written a new post that shows the way to accomplish the same thing in the functional style.) 

You can, of course, use C# anonymous types to create types that are more than simple tuples. For example, you can nest anonymous types to create an object graph:

var PurchaseOrder = new {
PurchaseOrderNumber = "99503",
OrderDate = DateTime.Parse("1999-10-20"),
Addresses = new [] {
new {
AddressType = "Shipping",
Name = "Alice Smith",
Street = "123 Maple Street",
City = "Mill Valley",
State = "CA",
Zip = "90952",
Country = "USA"
},
new {
AddressType = "Billing",
Name = "Robert Smith",
Street = "8 Oak Avenue",
City = "Old Town",
State = "PA",
Zip = "95819",
Country = "USA",
}
},
Comment = "Hurry, my lawn is going wild",
Items = new [] {
new {
PartNumber = "872-AA",
ProductName = "Lawnmower",
Quantity = 1,
USPrice = 148.95,
Comment = "Confirm this is electric",
ShipDate = DateTime.MinValue
},
new {
PartNumber = "926-AA",
ProductName = "Baby Monitor",
Quantity = 2,
USPrice = 39.98,
Comment = (string)null,
ShipDate = DateTime.Parse("1999-05-21")
}
}
};

This also has applicability with LINQ to XML. For example, you can use a small method that uses reflection to populate an XML tree. The following looks for public properties in the type and iterates over them, creating XElement objects. If a property implements the IEnumerable interface, then the method uses recursion to iterate over the children of the property.

static void ObjectGraphToXElement(XElement parent, object o) {
MemberInfo[] members =
o.GetType().GetMembers(BindingFlags.Public |
BindingFlags.Instance);
foreach (MemberInfo m in members) {
PropertyInfo p = m as PropertyInfo;
if (p != null) {
Type t = p.PropertyType;
object val = p.GetValue(o, null);
if (val != null) {
if (t.IsValueType || t == typeof(string))
parent.Add(new XElement(m.Name, val));
else {
XElement newParent = new XElement(m.Name);
parent.Add(newParent);
foreach (var v in (val as IEnumerable))
ObjectGraphToXElement(newParent, v);
}
}
}
}
}

The following code uses ObjectGraphToXElement to create an XML tree:

XElement po = new XElement("PurchaseOrder");
ObjectGraphToXElement(po, PurchaseOrder);
Console.WriteLine(po);

When run, it produces the following output:

<PurchaseOrder>
<PurchaseOrderNumber>99503</PurchaseOrderNumber>
<OrderDate>1999-10-20T00:00:00</OrderDate>
<Addresses>
<AddressType>Shipping</AddressType>
<Name>Alice Smith</Name>
<Street>123 Maple Street</Street>
<City>Mill Valley</City>
<State>CA</State>
<Zip>90952</Zip>
<Country>USA</Country>
<AddressType>Billing</AddressType>
<Name>Robert Smith</Name>
<Street>8 Oak Avenue</Street>
<City>Old Town</City>
<State>PA</State>
<Zip>95819</Zip>
<Country>USA</Country>
</Addresses>
<Comment>Hurry, my lawn is going wild</Comment>
<Items>
<PartNumber>872-AA</PartNumber>
<ProductName>Lawnmower</ProductName>
<Quantity>1</Quantity>
<USPrice>148.95</USPrice>
<Comment>Confirm this is electric</Comment>
<ShipDate>0001-01-01T00:00:00</ShipDate>
<PartNumber>926-AA</PartNumber>
<ProductName>Baby Monitor</ProductName>
<Quantity>2</Quantity>
<USPrice>39.98</USPrice>
<ShipDate>1999-05-21T00:00:00</ShipDate>
</Items>
</PurchaseOrder>

Pretty cool, eh?