views:

1483

answers:

3

I'm working with an existing XML document which has a structure (in part) like so:

<Group>
    <Entry>
        <Name> Bob </Name>
        <ID> 1 </ID>
    </Entry>
    <Entry>
        <Name> Larry </Name>
    </Entry>
</Group>

I'm using LINQ to XML to query the XDocument to retrieve all these entries as follows:

var items = from g in xDocument.Root.Descendants("Group").Elements("Entry")
    select new
    {
        name = (string)g.element("Name").Value,
        id = g.Elements("ID").Count() > 0 ? (string)g.Element("ID").Value : "none"
    };

The "ID" elements aren't always there and so my solution to this was the Count() jazz above. But I'm wondering if someone has a better way to do this. I'm still getting comfortable with this new stuff and I suspect that there may be a better way to do this than how I'm currently doing it.

Is there a better/more preferred way to do what I want?

+2  A: 

In a similar situation I used an extension method:

    public static string OptionalElement(this XElement actionElement, string elementName)
    {
        var element = actionElement.Element(elementName);
        return (element != null) ? element.Value : null;
    }

usage:

    id = g.OptionalElement("ID") ?? "none"
Cristian Libardo
Eoin Campbell
I like it - thanks.
itsmatt
ValueOrDefault(this XElement actionElement, string elementName, string defaultValue) would be even tidier.
Will
It's actually totally unnecessary. The explicit string conversion operator already does this: http://msdn.microsoft.com/en-us/library/bb155263.aspx
Jacob Carpenter
+1  A: 

How about:

var items = from g in xDocument.Root.Descendants("Group").Elements("Entry")
            let idEl = g.Element("ID")
            select new
            {
                name = (string)g.element("Name").Value,
                id = idEl == null ? "none" : idEl.Value;
            };

if this barfs, then FirstOrDefault() etc might be useful, else just use an extension method (as already suggested).

Marc Gravell
+5  A: 

XElement actually has interesting explicit conversion operators that do the right thing in this case.

So, you rarely actually need to access the .Value property.

This is all you need for your projection:

var items =
    from g in xDocument.Root.Descendants("Group").Elements("Entry")
    select new
    {
        name = (string) g.Element("Name"),
        id = (string) g.Element("ID") ?? "none",
    };

And if you'd prefer to use the value of ID as an integer in your anonymous type:

var items =
    from g in xDocument.Root.Descendants("Group").Elements("Entry")
    select new
    {
        name = (string) g.Element("Name"),
        id = (int?) g.Element("ID"),
    };
Jacob Carpenter
Thanks, Jacob. Interesting.
itsmatt
see http://www.hanselman.com/blog/ImprovingLINQCodeSmellWithExplicitAndImplicitConversionOperators.aspx for Hanselman's take on it
Jacob Carpenter