65.9K
CodeProject is changing. Read more.
Home

LINQ with Multiple Child Objects

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Feb 19, 2013

CPOL

2 min read

viewsIcon

12137

Pulling XML data into your objects

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()) _
                }

  ' LINQPad function emulation
  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.

' LINQPad function emulation
xSelect.Dump()

Display the object.