Лучший LINQ-to-XML запрос для выбора узлов на основе свойств дочерних узлов? - PullRequest
3 голосов
/ 24 апреля 2009

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

<?xml version="1.0" encoding="UTF-8"?>
<FamilyTree>
  <Parent name="Ken">
    <Child name="Lorna">
      <Grandchild name="Andrew"/>
      <Grandchild name="Brian"/>
    </Child>
    <Child name="Mike">
      <Grandchild name="Ann"/>
      <Grandchild name="Beth"/>
    </Child>
  </Parent>
  <Parent name="Norma">
    <Child name="Owen">
      <Grandchild name="Charles"/>
    </Child>
    <Child name="Peter">
      <Grandchild name="Charlotte"/>
    </Child>
  </Parent>
  <Parent name="Quinn">
    <Child name="Robert">
      <Grandchild name="Debbie"/>
      <Grandchild name="Eric"/>
    </Child>
    <Child name="Susan">
      <Grandchild name="Frank"/>
    </Child>
  </Parent>
  <Parent name="Tom">
    <Child name="Ursula">
      <Grandchild name="George"/>
      <Grandchild name="Harriet"/>
    </Child>
    <Child name="Victor">
      <Grandchild name="Ian"/>
      <Grandchild name="Juliet"/>
    </Child>
  </Parent>
</FamilyTree>

Я пытаюсь выбрать всех «родителей» с ребенком, у которого есть по крайней мере двое детей («внуков»). Обратите внимание, что я не ищу "Родителей" как минимум с двумя "Внуками [ren]".

Следующий запрос LINQ работает, но я чувствую, что он не самый элегантный.

IEnumerable<XElement> parents = (from c in familyTreeElement.Descendants("Child")
                                 where c.Elements().Count() > 1
                                 select c.Parent).Distinct();

Есть ли лучший способ указать это?

Ответы [ 3 ]

5 голосов
/ 24 апреля 2009

Хммм ... Мне трудно точно обвести голову :))

Обычно, чтобы выяснить, есть ли какие-либо элементы, я бы использовал Any - но вы хотите увидеть, есть ли хотя бы два элемента. Нам все еще не нужно использовать Count, потому что наличие хотя бы двух элементов - это то же самое, что пропустить элемент и посмотреть, есть ли еще какие-либо элементы. Итак ...

var parents = familyTreeElement.Elements("Parent")
    .Where(parent => parent.Elements("Child").Any(
                     child => child.Elements("Grandchild").Skip(1).Any()));

Я думаю, что это работает - и на самом деле это не читает слишком плохо:

Для каждого родителя посмотрите, есть ли у любого из них детей любых (больших) детей после игнорирования первого (большого) ребенка.

Я подозреваю, что использование XPath (согласно ответу Марка) будет наиболее читабельным вариантом.

2 голосов
/ 24 апреля 2009

Ааа, правка (2 внука) помогает; -p

Хотя XDocument полезно, иногда мне не хватает XPath / XQuery. С XmlDocument вы можете просто использовать doc.DocumentElement.SelectNodes("Parent[Child/Grandchild[2]]").

0 голосов
/ 24 апреля 2009

Я не знаю «SQL-подобный» синтаксис, чтобы гарантировать, что я получу правильный синтаксис, если напишу его таким образом, но вы хотите использовать .Any() вместо .Count(), и если вы выберите по-другому, вам не нужно Distinct() в конце. Попробуйте это:

IEnumerable<XElement> parents =
    familyTreeElement.Elements("Parent").Where(
        parent => parent.Elements("Child").Any(
            child => child.Elements().Count() >= 2));

РЕДАКТИРОВАТЬ: Если вы хотите убедиться, что есть по крайней мере 2, вы в значительной степени должны использовать .Count().

...