views:

52

answers:

1

XML sample (original link):

<records>
  <record index="1">
    <property name="Username">Sven</property>
    <property name="Domain">infinity2</property>
    <property name="LastLogon">12/15/2009</property>
  </record>
  <record index="2">
    <property name="Username">Josephine</property>
    <property name="Domain">infinity3</property>
    <property name="LastLogon">01/02/2010</property>
  </record>
  <record index="3">
    <property name="Username">Frankie</property>
    <property name="Domain">wk-infinity9</property>
    <property name="LastLogon">10/02/2009</property>
  </record>
</records>

I'm wanting to get an instance of a class per record in the xml.

i found similar examples in here but they only had a root, then one element deep. It works, right up until i put that other element in. i want to be able to do something like

foreach(Record rec in myVar)
{
Console.WriteLine("ID: {0} User:{1} Domain:{2} LastLogon:{3}",rec.Index, rec.Username, rec.Domain, rec.LastLogon);
}
+3  A: 

EDIT: updated code with ToDictionary approach for clarity and efficiency.

You can try the following sample. If you remove Record from the select new Record line it will result in an anonymous type and still work. Your Record class should have a default parameterless constructor to use the object initializer if you have provided other constructors (it will also work if you have no constructors). Otherwise you can use the available constructors instead of the object initializer.

Note that the use of Single() and Value assume the XML is well formed without any missing elements.

var xml = XElement.Parse(@"<records>
 <record index=""1"">
   <property name=""Username"">Sven</property>
   <property name=""Domain"">infinity2</property>
   <property name=""LastLogon"">12/15/2009</property>
 </record>
 <record index=""2"">
   <property name=""Username"">Josephine</property>
   <property name=""Domain"">infinity3</property>
   <property name=""LastLogon"">01/02/2010</property>
 </record>
 <record index=""3"">
   <property name=""Username"">Frankie</property>
   <property name=""Domain"">wk-infinity9</property>
   <property name=""LastLogon"">10/02/2009</property>
 </record>
</records>");

var query = from record in xml.Elements("record")
        let properties = record.Elements("property")
                               .ToDictionary(p => p.Attribute("name").Value, p => p.Value)
        select new Record
        {
            Index = record.Attribute("index").Value,
            Username = properties["Username"],
            Domain = properties["Domain"],
            LastLogon = properties["LastLogon"]
        };

foreach(var rec in query)
{
    Console.WriteLine("ID: {0} User:{1} Domain:{2} LastLogon:{3}", rec.Index, rec.Username, rec.Domain, rec.LastLogon);
}

EDIT: I've updated the code sample above with the ToDictionary approach which is cleaner and quicker. Based on my benchmarking efforts the fastest was ToDictionary, followed by Func, and then the Where approach.

Original Query

var query = from record in xml.Elements("record")
            let properties = record.Elements("property")
            select new Record
            {
                Index = record.Attribute("index").Value,
                Username = properties.Where(p => p.Attribute("name").Value == "Username").Single().Value,
                Domain = properties.Where(p => p.Attribute("name").Value == "Domain").Single().Value,
                LastLogon = properties.Where(p => p.Attribute("name").Value == "LastLogon").Single().Value
            };

Query with Func

Redundancy of the original query can be reduced by using the following code:

Func<XElement, string, string> GetAttribute =
          (e, property) => e.Elements("property")
                            .Where(p => p.Attribute("name").Value == property)
                            .Single().Value;

var query = from record in xml.Elements("record")
            select new Record
            {
                Index = record.Attribute("index").Value,
                Username = GetAttribute(record, "Username"),
                Domain = GetAttribute(record, "Domain"),
                LastLogon = GetAttribute(record, "LastLogon")
            };
Ahmad Mageed
PERFICT! I've been tryin to figure this out for 2-3 days now. My forhead is sore and red. What books do you recomend reguarding LINQ (and C#).
Sunzaru Sven
@Sunzaru I like LINQ in Action (http://www.amazon.com/dp/1933988169/) or you may want to get a C# 4.0 / .NET 4.0 book to cover some of the new material. C# 4.0 in a Nutshell is good but it covers lots of material. I also recommend downloading LINQPad (http://www.linqpad.net/) and going through the Nutshell samples that come with it (made by the author of the book). You can also download the LINQ in Action samples through it. Perhaps do that first before buying a book :)
Ahmad Mageed
i tested it today there was an average process time of 120MS. but in those 120MS i was also looking for a few other indicators not mentioned above. fast.. slick and zomg.. sooo much better than "Edit->Find". thanks again!
Sunzaru Sven
@Sunzaru glad you found it useful. BTW take another look at the main code block. I added a `ToDictionary` approach which is faster than both the original and `Func` approach. I also find it more readable.
Ahmad Mageed