Search code examples
c#.netlinqlinq-to-xmlxelement

selecting some specific xml elements to list of anonymous objects


I have an xml document with main XElement "details" and several "detail" XElements, and for each detail Element i have also several "node" Element, this is a part of my xml document:

<details>
    <detail>
      <node>
        <key>HEADER ID</key>
        <value>D10</value>
      </node>
      <node>
        <key>PRODUCT NO</key>
        <value>9671834480D04  </value>
      </node>
      <node>
        <key>WIRE (CODE)</key>
        <value>AD8</value>
      </node>
      <node>
        <key>WIRE SIZE(CODE)</key>
        <value>047</value>
      </node>
      <node>
        <key>WIRE COLOR(CODE)</key>
        <value>30</value>
      </node>
      <node>
        <key>CUT LENGTH</key>
        <value>01910</value>
      </node>
    </detail>
    <detail>
      <node>
        ...
      </node>
        ...
    </detail>
        ...
<details>

I am trying to transform this xml part to a list of object that contains just 3 properties correspond on "Key" and "value" elements. for example for each detail element in details and for each node element in detail i want to get just 3 nodes where the key element equals "PRODUCT NO" or "WIRE KIND(CODE)" or "CUT LENGTH"?

this is my code, it works, but i think it is not suitable for performance :

var champs = 
    from detail in details
    let productNo = detail.Elements("node")
        .Where(k => k.Element("key")
        .Value == "PRODUCT NO")
        .Select(v => v.Element("value").Value)
        .First()
    let wireCode = detail.Elements("node")
        .Where(k => k.Element("key").Value == "WIRE (CODE)")
        .Select(v => v.Element("value").Value)
        .First()
    let cutLength = detail.Elements("node")
        .Where(k => k.Element("key").Value == "CUT LENGTH")
        .Select(v => v.Element("value").Value)
        .First()
    select new { ProductNo = productNo, WireCode = wireCode , CutLength = cutLength };

i think this is an example of select n+1 issue, because for each propety i must browse all nodes, how can i do the same with one loop ?


Solution

  • I would actually think about readability before performance, unless you know you have a performance issue. But even so, you can definitely improve the code. I would consider using ToDictionary to convert each detail element into a Dictionary<string, string>, then you can get the bits you want:

    var query = details.Select(d => d.Elements("node")
                                     .ToDictionary(n => n.Element("key").Value,
                                                   n => n.Element("value").Value))
                       .Select(x => new { ProductNo = x["PRODUCT NO"],
                                          WireCode = x["WIRE (CODE)"],
                                          CutLength = x["CUT LENGTH"] });
    

    It's then very easy to add extra properties as you need them.