#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"