10

I'm basically brand new to LINQ. I've looked around a lot on here and am pretty confused. I've seen some examples that allow me to strong type objects using LINQ but I don't really understand them because they're in C#, which I guess lets you do different things with LINQ(I think?).

Anyways, this is what I'm trying to do:

Dim productXML As XDocument = XDocument.Load( _
    Server.MapPath("~/App_Data/products.xml"))

Dim products As List(Of Product) = 'some query to select all products ?'

'set up Product properties here'
someProduct.ProductID = 'somehow get productid from XML'

EDIT - I just want to get a list of all the products from the XML doc and put them into a Generics list.

Doctor Jones
  • 21,196
  • 13
  • 77
  • 99
Jason
  • 51,583
  • 38
  • 133
  • 185

4 Answers4

12

Marc is right, VB lets you do lots of nice stuff. I'm a C# guy myself, but I just knocked up a VB solution to see how to do it for you. I've posted the code below and explained the key parts. I was very impressed with the features VB has for Xml!

I see in your code sample that you've already managed to load your Xml into an XDocument. Once you've done your XDocument.Load you can access the Xml document using some special syntax.

For starters we want to get all the products from the document; i.e. all < Product > elements. We need to do the following:

Dim products = productsDoc...<Product>

This says that you want all < Product > elements from the document. This gives us an IEnumerable collection of XElements.

Once we've pulled an individual product from the collection we'll want to access the product's values like it's name or price. To do that we need to do the following:

' this gets the value of the price element within a product
product.<Price>.Value

Here's a full example along with the expected output for you to look at:

Module Module1

    ' some products xml to use for this example
    Dim productsXml = <Xml>
                          <Product>
                              <Name>Mountain Bike</Name>
                              <Price>59.99</Price>
                          </Product>
                          <Product>
                              <Name>Arsenal Football</Name>
                              <Price>9.99</Price>
                          </Product>
                          <Product>
                              <Name>Formula One Cap</Name>
                              <Price>14.99</Price>
                          </Product>
                          <Product>
                              <Name>Robin Hood Bow</Name>
                              <Price>8.99</Price>
                          </Product>
                      </Xml>

    Sub Main()

        ' load the xml into an XDocument
        ' NOTE: this line isn't needed when using inline XML as per this example, 
        ' but I wanted to make this code easy to modify for reading in text files
        Dim productsDoc = System.Xml.Linq.XDocument.Parse(productsXml.ToString())

        ' get all <Product> elements from the XDocument
        Dim products = From product In productsDoc...<Product> _
                       Select product

        ' go through each product
        For Each product In products
            ' output the value of the <Name> element within product
            Console.WriteLine("Product name is {0}", product.<Name>.Value)
            ' output the value of the <Price> element within product
            Console.WriteLine("Product price is {0}", product.<Price>.Value)
        Next

    End Sub

End Module

Program output is:

Product name is Mountain Bike
Product price is 59.99
Product name is Arsenal Football
Product price is 9.99
Product name is Formula One Cap
Product price is 14.99
Product name is Robin Hood Bow
Product price is 8.99

I hope this has been helpful. If you'd like any more information please just ask :-)

It's hard to write something coherent at bedtime! :-)

KyleMit
  • 30,350
  • 66
  • 462
  • 664
Doctor Jones
  • 21,196
  • 13
  • 77
  • 99
  • 4
    Yes it's weird syntax. If you use one period it will only look for direct descendents of the node you're dereferencing from. The three periods says to check all nodes underneath the current node. – Doctor Jones Jun 30 '09 at 22:46
  • this worked great, although i would still be interested to know how to do it using strong-typed objects rather than anonymous – Jason Jun 30 '09 at 23:16
  • 2
    You can add intellisense to your xml by using the XML to Schema wizard in VS2008. There's a walkthrough on how to do it on MSDN here http://msdn.microsoft.com/en-us/vbasic/bb840042.aspx. This'll make it much easier to develop with, unfortunatley it isn't strongly typed. It's an improvement though! – Doctor Jones Jul 01 '09 at 09:02
  • 1
    Great example! Your creation of the productsDoc variable is redundant. You can query productsXml directly in exactly the same way. It will be of type XElement. – CoderDennis Oct 30 '09 at 14:15
  • 1
    Oh, in order for the compiler to see producsXml as an XElement, it either needs to be inside the Main sub or needs to be explicitly declared "As XElement". – CoderDennis Oct 30 '09 at 14:22
  • 1
    @Dennis Palmer, thanks for the compliment :-) Yes I realised that creating the productsDoc variable was redundant, but the guy who asked the question wasn't using inline XML. I decided to write it in a way that'd be easily adapted to read from a file instead; therefore I demonstrated how to use System.Xml.Linq.XDocument.Parse to load an XDocument. I wanted to make my example concise but include actual XML with it so it could be a fully working example. – Doctor Jones Nov 09 '09 at 12:04
3

Doctor Jones posted an excellent example!

To get a collection of named type objects as opposed to anonymous type objects (both are strongly typed) do this:

Module Module1

' some products xml to use for this example '
    Dim productsXml As XElement = _
    <Xml>
        <Product>
            <Name>Mountain Bike</Name>
            <Price>59.99</Price>
        </Product>
        <Product>
            <Name>Arsenal Football</Name>
            <Price>9.99</Price>
        </Product>
        <Product>
            <Name>Formula One Cap</Name>
            <Price>14.99</Price>
        </Product>
        <Product>
            <Name>Robin Hood Bow</Name>
            <Price>8.99</Price>
        </Product>
    </Xml>

Class Product

    Private _name As String
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Private _price As Double
    Public Property Price() As Double
        Get
            Return _price
        End Get
        Set(ByVal value As Double)
            _price = value
        End Set
    End Property

End Class

Sub Main()

    ' get an IEnumerable of Product objects '
    Dim products = From prod In productsXml...<Product> _
                   Select New Product With {.Name = prod.<Name>.Value, .Price = prod.<Price>.Value}

    ' go through each product '
    For Each prod In products
        ' output the value of the <Name> element within product '
        Console.WriteLine("Product name is {0}", prod.Name)
        ' output the value of the <Price> element within product '
        Console.WriteLine("Product price is {0}", prod.Price)
    Next

End Sub

End Module
CoderDennis
  • 13,642
  • 9
  • 69
  • 105
1

OK, how about this?

Dim productXML As XDocument = XDocument.Load( _    
    Server.MapPath("~/App_Data/products.xml"))    
'
For Each product as Product In productXML.Document.Elements("Product")
    'do something with each product
Next
KyleMit
  • 30,350
  • 66
  • 462
  • 664
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
  • that link is for xml construction? i'm not looking to build XML... i've already done that. it's virtually static at this point. i need to read it and parse it into objects and then into a generics list. – Jason Jun 30 '09 at 21:59
  • how do i get "item" to be a "Product" as is the type of List(Of Product)? – Jason Jun 30 '09 at 22:04
  • i get this error under the "productXML" in "From item In productXML Select item": Expression of type System.XML.Linq.XDocument is not queryable. Make sure you are not missing an assembly reference and/or namespace import for the LINQ provider... but I imported System.Linq.... – Jason Jun 30 '09 at 22:10
  • Well, maybe you don't need the Linq query at all then. --> For Each product As Product in productXML... – Robert Harvey Jun 30 '09 at 22:15
  • thanks... tried: For Each item As Product In productXML but received the following error: Expression is of type System.XML.Linq.XDocument, which is not a collection type... :( – Jason Jun 30 '09 at 22:16
  • 1
    See my edit. I think you just need to extract a collection of document elements from the XDocument, and iterate through those. – Robert Harvey Jun 30 '09 at 22:35
1

I may be a little late to the party here, but I can't believe nobody has offered the XmlSerializer option:

Public Class Product
    Property Description As String
    Property Price As Double

    Public Shared Function FromXml(serverPath As String) As IEnumerable(Of Product)

       Using fs = IO.File.OpenRead(serverPath)
           Dim p As New Product
           Return New XmlSerializer(p.GetType).Deserialize(fs)
       End Using

    End Function

End Class

You can then return an iEnumerable of Product from the shared function:

dim listOfProducts = Product.FromXml(Server.MapPath("~/App_Data/products.xml"))

This doesn't use LinqToXml as such, but it deserializes the xml to an iEnumerable of product, which you can use Linq on as usual.