Потомки фильтра LINQ to XML C#

У меня есть xml, отправленный третьей стороной, и я хочу его проверить.

XElement xTree = XElement.Parse(@"<Container>
    <TrackingReferences>
        <TrackingReference>
          <TrackingName>Donny</TrackingName>
          <TrackingCodes>
            <TrackingCode>
                <Name></Name>
            </TrackingCode>
            <TrackingCode>
                <Name>DisplayThis</Name>
            </TrackingCode>
            <TrackingCode>
                <Name></Name>
            </TrackingCode>
          </TrackingCodes>
        </TrackingReference>
      </TrackingReferences>
    </Container>");

IEnumerable<XElement> childList = xTree.Element("TrackingReferences").Descendants("TrackingReference").Where(
    tr => (
            tr.Element("TrackingName") != null && !tr.Element("TrackingName").IsEmpty && !String.IsNullOrEmpty(tr.Element("TrackingName").Value) &&
            tr.Descendants("TrackingCodes").Any(
                tc => tc.HasElements &&
                    tc.Elements("TrackingCode").Any(
                        code => code.Element("Name") != null && !code.Element("Name").IsEmpty && !String.IsNullOrEmpty(code.Element("Name").Value)
                    )
            )
    )
);

Я не могу понять, как вернуть потомков, которых я хотел бы.

Моя проблема заключается в том, что я хочу, чтобы элемент TrackingReference содержал потомков TrackingCode только тогда, когда этот TrackingCode имеет Name. элемент, который не является нулевым или пустым.

Пример ниже возвращает:

<TrackingReference>
  <TrackingName>Donny</TrackingName>
  <TrackingCodes>
    <TrackingCode>
      <Name></Name>
    </TrackingCode>
    <TrackingCode>
      <Name>DisplayThis</Name>
    </TrackingCode>
    <TrackingCode>
      <Name></Name>
    </TrackingCode>
  </TrackingCodes>
</TrackingReference>

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

<TrackingReference>
      <TrackingName>Donny</TrackingName>
      <TrackingCodes>
        <TrackingCode>
          <Name>DisplayThis</Name>
        </TrackingCode>
      </TrackingCodes>
    </TrackingReference>

Это первый раз, когда я пробовал запрос LINQ к XML, поэтому любые советы о том, как сделать запрос более чистым/эффективным, будут высоко оценены, или если я пойду по этому пути неправильно.


person strangerinthealps    schedule 18.06.2014    source источник
comment
Вам действительно нужна модифицированная версия элемента TrackingReference или вас устроит последовательность элементов TrackingCode с непустым именем? Последнее существенно проще.   -  person Jon Skeet    schedule 18.06.2014
comment
Предполагая, что вам нужен обрезанный документ, преобразование Xsl может лучше подойти для этой проблемы?   -  person StuartLC    schedule 18.06.2014
comment
@JonSkeet - последний; последовательность элементов TrackingCode с непустым именем, поэтому в приведенном выше примере будет возвращен только один элемент TrackingCode вместо трех элементов TrackingCode (поскольку запрос в настоящее время возвращает). Большое спасибо.   -  person strangerinthealps    schedule 18.06.2014
comment
@StuartLC - в конечном итоге я вполне могу перейти на XSLT, но, поскольку я мало занимался LINQ to XML, я хотел, чтобы задача лучше понять его   -  person strangerinthealps    schedule 18.06.2014


Ответы (2)


Хорошо, похоже, вам нужны элементы TrackingCode, а не элементы TrackingReference, так что на самом деле это довольно просто:

var query = doc.Descendants("TrackingReference")
               // TODO: Filter based on TrackingName if you want...
               .Descendants("TrackingCode")
               .Where(x => !string.IsNullOrEmpty((string) x.Element("Name"));

Здесь используется тот факт, что явное преобразование строки в XElement вернет null, если вы вызовете его с операндом null.

person Jon Skeet    schedule 18.06.2014
comment
Я искал элемент TrackingReference, но чтобы у него были только потомки TrackingCode с непустым именем. Я отредактировал вопрос, чтобы показать, что я надеялся получить обратно. Теоретически в исходном xml может быть много элементов TrackingReference, поэтому я хотел бы запустить фильтр для всех из них. Хороший совет с явным преобразованием строк. Спасибо за помощь - person strangerinthealps; 18.06.2014

На всякий случай, если вы хотите рассмотреть решение xslt, вы можете исключить пустые TrackingCode с преобразованием идентификатора и шаблоном для eat пустых узлов:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" />
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="TrackingCode[not(Name)] | TrackingCode[Name='']"/>
</xsl:stylesheet>

скрипт XslCake здесь

person StuartLC    schedule 18.06.2014