XPath для выбора последнего узла во вложенной структуре XML?

#html #xml #xpath

#HTML #xml #xpath

Вопрос:

Предположим, у меня есть этот XML:

 <div>
    <div>
       <div>
          <div>
          "Hello2"
          </div>
       </div>
    </div>
</div>
  

Но мои шаблоны могут измениться, и я хочу быть гибким в глубине, в которой div расположен элемент. Пример:

 <div>
    <div>
       <div>
        "Hello3"
       </div>
    </div>
</div>
  

Итак, как я могу получить внутренний текст из последнего элемента в XML из этой вложенной структуры XML?

Ответ №1:

Итак, как я могу получить внутренний текст из последнего элемента в XML из этой вложенной структуры XML?

Из опубликованного XML-документа кажется, что на самом деле задается вопрос:

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


I. Решение XPath 1.0 / XSLT 1.0:

Это выражение XPath при вычислении:

 //*[not(*)]
  

выбирает все элементы в документе, у которых нет другого дочернего элемента.

 (//*[not(*)])[last()]
  

выбирает последний такой самый внутренний узел.

Невозможно найти «самый глубокий элемент» с помощью одного выражения XPath 1.0 — это можно сделать с помощью простого преобразования XSLT 1.0. Приведенное ниже преобразование XSLT 1.0 копирует на вывод последний самый внутренний элемент XML-документа:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 
 <xsl:key name="kElemByDepth" match="*" use="count(ancestor::*)"/>

  <xsl:template match="/">
    <xsl:variable name="vMaxDepth">
        <xsl:apply-templates select="//*[not(*)]" mode="getMax">
           <xsl:sort select="count(ancestor::*)" data-type="number" order="descending"/>
        </xsl:apply-templates>
    </xsl:variable>
    
    <xsl:copy-of select="key('kElemByDepth', $vMaxDepth)[last()]"/>
  </xsl:template>
  
  <xsl:template match="*" mode="getMax">
    <xsl:if test="position() = 1">
      <xsl:value-of select="count(ancestor::*)"/>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>
  

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

 <div>
    <div>
       <div>
          <div>
          "Hello2"
          </div>
          <div>
          "Hello3"
          </div>
       </div>
    </div>
   <div>
     "Hello1"
    </div>
</div>
  

получен требуемый правильный результат:

 <div>
 "Hello3"
</div>
  

Если вам нужно только строковое значение этого элемента, просто замените:

 <xsl:copy-of select="key('kElemByDepth', $vMaxDepth)[last()]"/>
  

с

 <xsl:copy-of select="normalize-space(key('kElemByDepth', $vMaxDepth)[last()])"/>
  

II. Чистое решение XPath 2.0

Используйте это выражение XPath 2.0:

 normalize-space(//*[not(*)]
                     [not(count(ancestor::*) < //*[not(*)]/count(ancestor::*))][last()])
  

Проверка на основе XSLT 2.0:

Это преобразование вычисляет приведенное выше выражение XPath 2.0 и копирует в выходные данные результат этой оценки:

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

  <xsl:template match="/">
    <xsl:sequence select=
    "normalize-space(//*[not(*)]
                 [not(count(ancestor::*) amp;< //*[not(*)]/count(ancestor::*))][last()])"/>
  </xsl:template>
</xsl:stylesheet>
  

при применении к тому же документу XML (выше) снова получается тот же правильный, желаемый результат:

 "Hello3"