выбор некоторых определенных элементов xml для списка анонимных объектов

У меня есть xml-документ с основными «подробностями» XElement и несколькими «подробными» XElements, и для каждого подробного элемента у меня также есть несколько элементов «node», это часть моего XML-документа:

<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>

Я пытаюсь преобразовать эту xml-часть в список объектов, который содержит только 3 свойства, соответствующие элементам «Key» и «value». например, для каждого элемента детали в деталях и для каждого элемента узла в деталях я хочу получить только 3 узла, где ключевой элемент равен «НОМЕР ПРОДУКТА» или «ВИД ПРОВОДА (КОД)» или «ДЛИНА РЕЗКИ»?

это мой код, он работает, но я думаю, что он не подходит для производительности:

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 };

я думаю, что это пример проблемы выбора n + 1, потому что для каждого свойства я должен просматривать все узлы, как я могу сделать то же самое с одним циклом?


person YouneL    schedule 15.12.2014    source источник


Ответы (1)


На самом деле я бы подумал о читабельности перед производительностью, если только вы не знаете, что у вас проблемы с производительностью. Но даже в этом случае вы определенно можете улучшить код. Я бы подумал об использовании ToDictionary для преобразования каждого элемента detail в Dictionary<string, string>, тогда вы сможете получить нужные биты:

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"] });

Затем очень легко добавить дополнительные свойства по мере необходимости.

person Jon Skeet    schedule 15.12.2014
comment
Я вставляю ваш запрос в linqPad и получаю эту ошибку: Невозможно применить индексирование с помощью [] к выражению типа «System.Collections.Generic.KeyValuePair‹string,string›» - person YouneL; 15.12.2014
comment
Я все еще получаю ту же ошибку, есть ли другой способ без преобразования XElements в словарь? - person YouneL; 16.12.2014
comment
@younel: У вас не должно было быть той же ошибки - у вас должна была быть немного другая ошибка :) Сейчас я исправил опечатку, и у меня это работает ... это предполагает, что details - это IEnumerable<XElement>, где каждый элемент является элементом <detail>. - person Jon Skeet; 16.12.2014
comment
да, это работает, если ключевой элемент уникален, но у меня есть несколько ключевых элементов с одинаковым содержимым, запрос останавливается в функции ToDictionary и выдается исключение ArgumentException: элемент с таким же ключом уже добавлен. в любом случае, я считаю, что проблема решена. думает - person YouneL; 16.12.2014
comment
@younel: Вы никогда не упоминали об этом, поэтому я не знаю, как я должен был догадаться об этом. В этом случае вы можете использовать ToLookup. - person Jon Skeet; 16.12.2014