Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

LINQ with Multiple Child Objects

0.00/5 (No votes)
19 Feb 2013CPOL2 min read 12.1K  
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.

VB.NET
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.

VB.NET
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.

VB.NET
Private Enum AutoTypes
  Car
  Pickup
  TractorTrailer
End Enum

I like to set up module level variables in an initialization routine.

VB.NET
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:

VB.NET
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!

VB.NET
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.

VB.NET
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.

VB.NET
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.

VB.NET
.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".

VB.NET
.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.

VB.NET
' LINQPad function emulation
xSelect.Dump()

Display the object.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)