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>

Сделать примечание:

  1. Использование правила идентификации для копирования каждого узла исходного XML-документа.

  2. Переопределение правила идентификации конкретным шаблоном, который выполняет вставку нового текста.

  3. Как правило идентификации применяется (на каждом узле) и вызывается по имени (для конкретной необходимости).

Редактировать: исправлена ​​ошибка, из-за которой я не смог установить правильный синтаксис

<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>
Другие вопросы по тегам