Выбор из xml на основе атрибутов в нескольких подузлах

#c# #linq

#c# #linq

Вопрос:

У меня есть следующий XML-файл:

 <?xml version="1.0" encoding="utf-8"?>
<nodes>
  <node id="1">
    <subnode name="a" value="1" />
    <subnode name="b" value="2" />
  </node>
  <node id="2">
    <subnode name="a" value="2" />
    <subnode name="b" value="2" />
  </node>
  <node id="3">
    <subnode name="a" value="1" />
    <subnode name="b" value="1" />
  </node>
  <node id="4">
    <subnode name="a" value="1" />
    <subnode name="b" value="2" />
  </node>
</nodes>
  

Мне нужно выбрать идентификаторы узлов, которые имеют оба a=1 и b=2 (в данном случае узел 1 и узел 4).

Я бы предпочел сделать это с помощью linq, и у меня есть следующий код для выбора тех, которые есть a=1 . Как мне расширить код, чтобы также выполнить второе требование?

 var document = XDocument.Load(@"c:tempsubnodes.xml");

var x = from topnode in document.Descendants("nodes")
   let nodes = topnode.Descendants("node") from n in nodes
   let subnodes = n.Descendants("subnode") from s in subnodes
   where s.Attribute("name").Value == "a" amp;amp; s.Attribute("value").Value == "1"
   select n.Attribute("id").Value;
  

РЕДАКТИРОВАТЬ: я создал скрипку .NET здесь: https://dotnetfiddle.net/mOg3wv

Комментарии:

1. where (s.Attribute("name").Value == "a" amp;amp; s.Attribute("value").Value == "1") amp;amp; (s.Attribute("name").Value == "b" amp;amp; s.Attribute("value").Value == "2")

2. @Aarif: атрибут «name» не может быть одновременно a и b . При этом узлы не выбираются.

3. or в этом случае используйте условие ||

4. @Aarif: это также выбирает узел 2 и 3.

5. понял, позвольте мне обновить запрос

Ответ №1:

Вы можете использовать следующее.

 var result = document.Descendants("node").Where(x=>x.Descendants("subnode")
            .All(c=>(c.Attribute("name").Value == "a" amp;amp; c.Attribute("value").Value=="1") || 
                    (c.Attribute("name").Value == "b" amp;amp; c.Attribute("value").Value=="2")))
            .Select(x=>x.Attribute("id").Value);
  

Вывод

 1 
4 
  

Ответ №2:

ОБНОВЛЕНИЕ 2:
вот решение на основе LINQ для этого

  var nodes = document.Descendants("nodes").Descendants("node");

 return (from node in nodes
        let aSubNode = node.Descendants("subnode")
          .FirstOrDefault(a => a.Attribute("name")?.Value == "a" amp;amp; a.Attribute("value")?.Value == "1")
        let bSubNode = node.Descendants("subnode")
          .FirstOrDefault(a => a.Attribute("name")?.Value == "b" amp;amp; a.Attribute("value")?.Value == "2")
        where aSubNode != null amp;amp; bSubNode != null
        select node.Attribute("id")?.Value).ToList();
  

ОБНОВЛЕНИЕ 1:
Я обновил код, чтобы считывать id значение атрибута из node элемента вместо subnode .

ОРИГИНАЛ:
ниже приведен не весь LINQ, но это должно решить вашу проблему

 var nodes = document.Descendants("nodes").Descendants("node");

var ids=new List<string>();

foreach (var node in nodes)
{
       var aSubNode = node.Descendants("subnode")
            .FirstOrDefault(a => a.Attribute("name")?.Value == "a" amp;amp; a.Attribute("value")?.Value == "1");
       var bSubNode=node.Descendants("subnode")
           .FirstOrDefault(a => a.Attribute("name")?.Value == "b" amp;amp; a.Attribute("value")?.Value == "2");

       if (aSubNode==null || bSubNode==null)
           continue;
       ids.Add(node.Attribute("id")?.Value);
}
  

Комментарии:

1. Боюсь, это возвращает мне список из 4 нулевых строк. Но спасибо за ваши усилия.

2. позвольте мне проверить это

3. @PalleDue вы можете попробовать сейчас?, Я обновил ответ.

4. Да, это дает мне ожидаемый ответ. Однако я немного неохотно принимаю это как ответ. Это не совсем элегантный запрос linq, который я искал. Я подожду и посмотрю, появится ли лучшее решение.

5. конечно, @PalleDue, я попытался сначала использовать все LINQ , я попробую, смогу ли я преобразовать это в LINQ