Xpath: отфильтровывать дочерние элементы

Я ищу выражение xpath, которое отфильтровывает определенных потомков. Ребенок должен содержать узел CCC с буквой B.

Источник:

<AAA>
    <BBB1>
        <CCC>A</CCC>
    </BBB1>       
    <BBB2>
        <CCC>A</CCC>
    </BBB2>
    <BBB3>
        <CCC>B</CCC>
    </BBB3>
    <BBB4>
        <CCC>B</CCC>
    </BBB4>
</AAA>

Это должно быть результатом:


<AAA>
    <BBB3>
        <CCC>B</CCC>
    </BBB3>
    <BBB4>
        <CCC>B</CCC>
    </BBB4>
</AAA>

Надеюсь, кто-нибудь может мне помочь.

Jos

4 ответа

XPath - это язык запросов для документов XML. Таким образом, он может выбирать только узлы из существующих документов XML - он не может изменять документ XML или создавать новый документ XML.

Используйте XSLT для преобразования XML-документа и создания нового XML-документа из него.

В этом конкретном случае:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*/*[not(CCC = 'B')]"/>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному документу XML:

<AAA>
    <BBB1>
        <CCC>A</CCC>
    </BBB1>
    <BBB2>
        <CCC>A</CCC>
    </BBB2>
    <BBB3>
        <CCC>B</CCC>
    </BBB3>
    <BBB4>
        <CCC>B</CCC>
    </BBB4>
</AAA>

желаемый, правильный результат получается:

<AAA>
   <BBB3>
      <CCC>B</CCC>
   </BBB3>
   <BBB4>
      <CCC>B</CCC>
   </BBB4>
</AAA>

Чтобы выбрать все нужные элементы и текстовые узлы, используйте этот XPATH:

//node()[.//CCC[.='B']
      or self::CCC[.='B']
      or self::text()[parent::CCC[.='B']]]

Это может быть достигнуто с помощью более простого / легкого использования XPATH с измененным XSLT-преобразованием идентичности:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" />

    <!--Empty template for the content we want to redact -->
    <xsl:template match="*[CCC[not(.='B')]]" />

    <!--By default, copy all content forward -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Попробуй это,

         "//CCC[text() = 'B']"

Он должен дать все узлы CCC, где внутренний текст равен B.

Если вы хотите получить AAA, BBB3 и BBB4, вы можете использовать следующие

//*[descendant::CCC[text()='B']]

Если BBB3 и BBB4 только тогда

//*[CCC[text()='B']]
Другие вопросы по тегам