XSLT: добавить внутренний текст узла
Это немного версия другого вопроса, размещенного здесь: XSLT: изменить внутренний текст узла
Представьте, что я использую XSLT для преобразования документа:
<a>
<b/>
<c/>
</a>
в это:
<a>
<b/>
<c/>
Hello world
</a>
В этом случае я не могу использовать ни
<xsl:strip-space elements="*"/>
элемент или предикат [normalize-space()!= ''], поскольку там, где мне нужно поместить новый текст, нет текста. Есть идеи? Благодарю.
3 ответа
Вот что я бы сделал:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- identity template to copy everything unless otherwise noted -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- match the first text node directly following a <c> element -->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]">
<!-- ...and change its contents -->
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
Обратите внимание, что текстовые узлы содержат "окружающие" пробелы - в примере XML в вопросе сопоставленный текстовый узел является только пробелом, поэтому вышеприведенное работает. Он перестанет работать, как только входной документ будет выглядеть так:
<a><b/><c/></a>
потому что здесь нет текстового узла, следующего за <c>
, Так что, если это слишком хрупко для вашего варианта использования, альтернативой будет:
<!-- <c> nodes get a new adjacent text node -->
<xsl:template match="c">
<xsl:copy-of select="." />
<xsl:text>Hello world</xsl:text>
</xsl:template>
<!-- make sure to remove the first text node directly following a <c> node-->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]" />
В любом случае, материал, подобный описанному выше, проясняет, почему лучше всего избегать смешивания текстовых узлов и узлов элементов. Это не всегда возможно (см. XHTML). Но когда у вас есть такая возможность, а XML должен быть просто контейнером для структурных данных, отказ от смешанного контента делает вашу жизнь проще.
Это преобразование вставляет нужный текст (для общности) после элемента с именем a7
:
<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()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a7">
<xsl:call-template name="identity"/>
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
при применении к этому документу XML:
<a>
<a1/>
<a2/>
.....
<a7/>
<a8/>
</a>
желаемый результат получается:
<a>
<a1/>
<a2/>
.....
<a7/>Hello world
<a8/>
</a>
Сделать примечание:
Использование правила идентификации для копирования каждого узла исходного XML-документа.
Переопределение правила идентификации конкретным шаблоном, который выполняет вставку нового текста.
Как правило идентификации применяется (на каждом узле) и вызывается по имени (для конкретной необходимости).
Редактировать: исправлена ошибка, из-за которой я не смог установить правильный синтаксис
<xsl:template match='a'>
<xsl:copy-of select="." />
<xsl:text>Hello World</xsl:text>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>