views:

249

answers:

3

This snippet is from this answer

var reports = from report in xml.Descendants("report")
    where report.Element("name").Value.Contains("Adjustment Report")
    select new {
        Name = report.Element("name").Value,
        Extension = report.Element("extension").Value,
        FileType = report.Element("filetype").Value,
        Fields = report.Elements("field")
            .Select(f => new {
                Name = f.Attribute("name").Value, 
                Type = f.Attribute("type").Value 
            }).ToArray()
    };

For the life of me I cannot figure out the syntax for this part in vb.net:

        Fields = report.Elements("field")
            .Select(**f =>** new {
                Name = f.Attribute("name").Value, 
                Type = f.Attribute("type").Value 
            }).ToArray()

What I'm trying to accomplish - my xml looks like this:

<items>
 <item>
  <id>data</id>
  <foto>
   <fotoname>img1.jpg</fotoname>
   <fotoorder>1</fotoorder>
  </foto>
  <foto>
   <fotoname>img2.jpg</fotoname>
   <fotoorder>2</fotoorder>
  </foto>
 </item>
</items>

I need my object to have a List (or collection of any kind) of foto elements.

A: 

Hah read this yesterday (if I'm right - and I'm probably not 100% on all the syntax...)

Fields = report.Elements("field")
    .Select(Function(f) new { 
        Name = f.Attribute("name").Value,
        Type = f.Attribute("type").Value
    }).ToArray()

The key bit - if my memory is working right - is changing "f =>" to "Function(f)"

Murph
I think you're right but I'm still not where I need to be. I'll clarify my question.
Vnuk
+3  A: 

LINQ to XML is one of the areas where VB.NET offers a completely different syntax than C#. You can use the same method chaining, but I prefer the VB.NET LINQ syntax that looks like this:

Sub Main()
    Dim myXml = <items>
                    <item>
                        <id>data</id>
                        <foto>
                            <fotoname>img1.jpg</fotoname>
                            <fotoorder>1</fotoorder>
                        </foto>
                        <foto>
                            <fotoname>img2.jpg</fotoname>
                            <fotoorder>2</fotoorder>
                        </foto>
                    </item>
                </items>

    Dim fotoElementsQuery = From f In myXml...<foto> _
                            Select f

    Dim fotoAnonymousTypeQuery = From f In myXml...<foto> _
                                 Select f.<fotoname>.Value, f.<fotoorder>.Value

    Dim fotoNamedTypeQuery = From f In myXml...<foto> _
                             Select New Foto With {.Name = f.<fotoname>.Value, .Order = f.<fotoorder>.Value}

End Sub

Public Class Foto

    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 _order As Integer
    Public Property Order() As Integer
        Get
            Return _order
        End Get
        Set(ByVal value As Integer)
            _order = value
        End Set
    End Property

End Class

This gives you 3 different types of IEnumerable results.

  1. fotoElementsQuery will be of type IEnumerable(Of XElement)
  2. fotoAnonymousTypeQuery will be of type IEnumerable(Of <anonymous type>). The elements of the anonymous type will take on the names of the xml elements -- fotoname and fotoorder.
  3. fotoNamedTypeQuery will be of type IEnumeragle(Of Foto)

The LINQ queries haven't actually executed yet in the above code. In order to get a List (and to execute the query) call either the .ToList() or .ToArray() extension method.

Update: The best way to learn about the goodness that is LINQ (and LINQ to XML) in VB.NET is by watching the How Do I Video Series by Beth Massi. http://msdn.microsoft.com/en-us/vbasic/bb466226.aspx#linq

Dennis Palmer
You rule man. Do you have any links that explain this ... syntax? I've never seen it before.
Vnuk
Thanks for the link, will check it out.
Vnuk
+2  A: 

To flesh out some of the other answers:

In VB.NET, you can use XML Axis operators to simplify the method syntax. For example <root>..<child> is the same as XElement("root").Elements("child"). In this case, the child has to be directly under the root. If you want to find nodes regardless of where they exist in the child nodes you can either use .Descendents instead of .Elements, or the VB syntax with three dots as follows: <root>...<descendentNodeName>. If you want to access an attribute, use .@ as follows: <root>.@attributeName.

From Murph's response, you can re-write it in VB as follows:

Fields = (From f In report.<field> _
          Select Name = f.@name, Type = f.@type).ToArray()

This can also be written using the Lambda syntax as follows:

Fields = report.<field> _
         .Select(Function(f) New With { _
             Name = f.@name, Type = f.@type).ToArray()
Jim Wooley
Thanks for fleshing, it's been really helpful
Vnuk