I have been playing with LINQ for a little while. It has some very interesting features built into it. One of those features is pulling XML data into your objects. While the examples below are contrived, they still illustrate how to build child objects into a parent object.
First, I needed a parent
class. I am going to pick something that we can all understand: cars.
Private Class Auto
Public Type As AutoTypes
Public Brand As String
Public Model As String
Public Tires As List(Of Tire)
Public Options As IEnumerable(Of XElement)
Public SafetyFeatures As IEnumerable(Of XElement)
End Class
Pretty straightforward, except for those last two fields. These are going to hold XML data later.
Next, I needed a child class.
Private Class Tire
Public Size As String
Public Qty As Integer
Public Spare As Tire
End Class
I had to define my enumeration at the top of the Auto class.
Private Enum AutoTypes
Car
Pickup
TractorTrailer
End Enum
I like to set up module level variables in an initialization routine.
Private Sub Init()
Dim xSource As XElement
xSource = <Automotives>
<Automotive type="car" brand="Omnicron" model="X">
<Engine type="gas" cylinders="4" size="1.5liter"/>
<Tires size="215/70R15" qty="4">
<Spare size="donut" qty="1"/>
</Tires>
<OptionsIncluded>
<Radio brand="JVC" model="JVC1"/>
<Transmission type="Automatic" speeds="5"/>
<Seats material="cloth" color="brown"
heated="false" cooled="true"/>
<CruiseControl type="standard"/>
</OptionsIncluded>
</Automotive>
<Automotive type="car" brand="Omnicron" model="Z">
<Engine type="gas" cylinders="8" size="5.2liter"/>
<Tires size="205/70R17" qty="4">
<Spare size="donut" qty="1"/>
</Tires>
<OptionsIncluded>
<Radio brand="Bose" model="B1"/>
<Transmission type="Manual" speeds="8"/>
<Seats material="leather" color="brown"
heated="true" cooled="true"/>
<CruiseControl type="enh001"/>
<SafetyFeatures>
<SRS bags="22"/>
<Harness points="5"/>
<Brakes abs="true"/>
</SafetyFeatures>
</OptionsIncluded>
</Automotive>
</Automotives>
End Sub
In case you weren't aware of it, VB.NET allows you assign XML directly to an XElement
without having to parse it in code. Here's the equivalent C# code:
public void Init()
{
XElement xSource == new XElement("Automotives",
new XElement("Automotive", new XAttribute("type", "car"),
new XAttribute("brand", "Omnicron"),
new XAttribute("model", "X"),
new XElement("Engine", new XAttribute("type", "gas"),
new XAttribute("cylinders", "4"), new XAttribute("size", "1.5liter")),
new XElement("Tires", new XAttribute("size", "215/70R15"),
new XAttribute("qty", "4"),
new XElement("Spare", new XAttribute("size", "donut"),
new XAttribute("qty", "1"))),
new XElement("OptionsIncluded",
new XElement("Radio", new XAttribute("brand", "JVC"),
new XAttribute("model", "JVC1")),
new XElement("Transmission", new XAttribute("type", "Automatic"),
new XAttribute("speeds", "5")),
new XElement("Seats", new XAttribute("material", "cloth"), _
new XAttribute("color", "brown"),
new XAttribute("heated", "false"),
new XAttribute("cooled", "true")),
new XElement("CruiseControl", new XAttribute("type", "standard")))),
new XElement("Automotive", new XAttribute("type", "car"),
new XAttribute("brand", "Omnicron"), new XAttribute("model", "Z"),
new XElement("Engine", new XAttribute("type", "gas"),
new XAttribute("cylinders", "8"), new XAttribute("size", "5.2liter")),
new XElement("Tires", new XAttribute("size", "205/70R17"),
new XAttribute("qty", "4"),
new XElement("Spare", new XAttribute("size", "donut"),
new XAttribute("qty", "1"))),
new XElement("OptionsIncluded",
new XElement("Radio", new XAttribute("brand", "Bose"),
new XAttribute("model", "B1")),
new XElement("Transmission", new XAttribute("type", "Manual"),
new XAttribute("speeds", "8")),
new XElement("Seats", new XAttribute("material", "leather"),
new XAttribute("color", "brown"), _
new XAttribute("heated", "true"),
new XAttribute("cooled", "true")),
new XElement("CruiseControl", new XAttribute("type", "enh001")),
new XElement("SafetyFeatures",
new XElement("SRS", new XAttribute("bags", "22")),
new XElement("Harness", new XAttribute("points", "5")),
new XElement("Brakes", new XAttribute("abs", "true"))))));
}
Now that we have all of the setup stuff out of the way, we can get into the actual code!
Sub Main()
Init()
Dim xSelect = From a In xSource.<Automotive> _
Select New Auto With _
{ _
.Type = DirectCast([Enum].Parse(GetType(AutoTypes), a.@type, True), AutoTypes), _
.Brand = a.@brand, _
.Model = a.@model, _
.Tires = (From t In a.<Tires> Select New Tire _
With {.Size = t.@size, .Qty = t.@qty}).ToList(), _
.Options = (From o In a.<OptionsIncluded>.Elements()), _
.SafetyFeatures = (From s In a.<OptionsIncluded>.<SafetyFeatures>.Elements()) _
}
xSelect.Dump()
End Sub
Since the LINQ statement is comprised of a whole bunch of chained instructions, I am going to explain them one at a time.
Dim xSelect = From a In xSource.<Automotive> _
Create a variable where we can store the output of the LINQ statement. In case you are interested, the compiler will assign a type of IEnumerable(Of XElement)
to xSelect
. This portion of the LINQ statement (to the right of the equal sign) tells the compiler to get all of the elements and attributes that make up the Automotive nodes. Each Automotive node will be self-contained as you cycle through the enumeration.
Select New Auto With _
This instruction tells the compiler to create a new object of type Auto
that we are going to populate with data.
.Type = DirectCast([Enum].Parse(GetType(AutoTypes), a.@type, True), AutoTypes), _
We are starting to populate the object created above. DirectCast
converts a string
stored in the XML data into an Enum
value if it can find the matching name from our enumeration. a.@type
returns data from LINQ for an attribute named "type
".
.Tires = (From t In a.<Tires> Select New Tire With {.Size = t.@size, .Qty = t.@qty}).ToList(), _
This is the most complex instruction in the LINQ statement as it pulls data from the child element Tires
in the current Automotive
element, builds a new list of objects of the type Tire
, and finally, populating the generic list property Tires
. The .ToList()
method is called to convert the IEnumerable(Of XElement)
to a generic list. The last step of converting to a generic list was skipped for the final two properties.
xSelect.Dump()
Display the object.