tags:

views:

316

answers:

3

I have an XElement variable named content which consists of the following XML:

<content>
    <title>Contact Data</title>
    <p>This is a paragraph this will be displayed in front of the first form.</p>
    <form idCode="contactData" query="limit 10; category=internal"/>
    <form idCode="contactDataDetail" query="limit 10; category=internal">
     <title>Contact Data Detail</title>
     <description>This is the detail information</description>
    </form>
</content>

I now want to simply run through each of the level-1 nodes and parse them into objects. Back in C# 2.0 I use to do this with XmlReader, check the type of node, and parse it accordingly.

But what is the best way to parse the XML nodes with LINQ, I would expect something like this:

var contentItems = from contentItem in pageItem.content.DescendantNodes()
                   select new ContentItem
                   {
                       Type = contentItem.Element()
                   };

foreach (var contentItem in contentItems)
{
    switch (contentItem.Type)
    {
        case "title":
            ...(parse title)...
        case "p":
            ...(parse p)...
        case "form":
            ...(parse form)...
    }
}

where:

public class ContentItem
{
    public string Type { get; set; }
    public string IdCode { get; set; }
    public XElement Content { get; set; }
}
+1  A: 

Does it have to be XElement? I would (either manually, or via xsd.exe) just create classes that map to the element/attribute names - and use XmlSerializer - in particular via StringReader:

        Content content;
        using(StringReader sr = new StringReader(xml))
        using(XmlReader xr = XmlReader.Create(sr)) {
            XmlSerializer ser = new XmlSerializer(typeof(Content));
            content = (Content)ser.Deserialize(xr);
        }

(edit)

With entity classes:

[XmlRoot("content")]
public class Content {
    [XmlElement("title")]
    public string Title { get; set; }
    [XmlElement("p")]
    public string Description { get; set; }
    [XmlElement("form")]
    public List<ContentForm> Forms { get; set; }
}    
public class ContentForm {
    [XmlAttribute("idCode")]
    public string Id { get; set; }
    [XmlAttribute("query")]
    public string Query { get; set; }
    [XmlElement("title")]
    public string Title { get; set; }
    [XmlElement("description")]
    public string Description { get; set; }
}
Marc Gravell
A: 

With XML to LINQ one pulls specific data items out of the XML, rather than iterating through the XML looking for what is found.

var results = from node in XmlDocument.SomeContext(...)
              select new MyType {
                Prop1 = node.Element("One").Value,
                Prop2 = node.Element("One").Attributes().Where(
                      a => A.Value.Contains("Foo").Value
            };

If conditions are needed, then (extension) methods and arbitrary expressions can be used (null-coalescing operator, ??, can be very helpful to default).

Richard
so does that mean if order is important, then LINQ can really not be used to parse the XML? For instance, it is important in the above example that "title" be parsed before "p" since that is how they are going to appear when inserted as TextBlocks in the UserControl.
Edward Tanguay
@Edward: Arbitrary node relations can be checked using more complex LINQ expressions or XPath. But would need more detailed information to give an example (e.g. what happens if the p does not follow the title?)
Richard
this is just an xml that determines what gets displayed on a user control, so the items in the XML just get added in order which means that e.g. if I can't determine the order when parsing, I would have to add an attribute to every element called e.g. displayOrder="1", displayOrder="2" etc.
Edward Tanguay
@Edward: The nodes in the LINQ expression will be in order (i.e. the select clause will be called for each node matched so far in tuen), this can be clearly exploited in the select (or whatever consumer, e.g. a foreach statement) of the XNode sequence. That said, for stream based processing sticking with a reader (especially where it already works) might be better than shifting to the rather different paradigm of LINQ to XML.
Richard
A: 

I'd suggest inheriting XElement, and implement properties for the stuff you want in it. These properties shouldn't use backing fields, but rather work directly with the underlying XML element. That way, you'll keep object in sync with XML.

baretta