开发者

XSLT: add node inner text

This is a slightly version of other question posted here: XSLT: change node inner text

Imagine i use XSLT to transform the document:

<a>
  <b/>
  <c/>
</a>

into this:

<a>
  <b/>
  <c/>
  Hello world
</a>

In this case i can't use neither the

<xsl:strip-space elements="*"/> 

element or the [normalize-space() != ''] predicate since there is no text in t开发者_StackOverflow中文版he place where i need to put new text. Any ideas? Thanks.


Here is what I would do:

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

Note that text nodes contain "surrounding" whitespace - in the sample XML in the question the matched text node is whitespace only, which is why the above works. It will stop to work as soon as the input document looks like this:

<a><b/><c/></a>

because here is no text node following <c>. So if this is too brittle for your use case, an alternative would be:

<!-- <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]]" />

In any case, stuff like the above makes clear why intermixing of text nodes and element nodes is best avoided. This is not always possible (see XHTML). But when you have the chance and the XML is supposed to be purely a container for structural data, staying clear of mixed content makes your life easier.


This transformation inserts the desired text (for generality) after the element named 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>

when applied on this XML document:

<a>
  <a1/>
  <a2/>
  .....
  <a7/>
  <a8/>
</a>

the desired result is produced:

<a>
    <a1/>
    <a2/>
  .....
    <a7/>Hello world
    <a8/>
</a>

Do note:

  1. The use of the identity rule for copying every node of the source XML document.

  2. The overriding of the identity rule by a specific template that carries out the insertion of the new text.

  3. How the identity rule is both applied (on every node) and called by name (for a specific need).


edit: fixed my fail to put proper syntax in.

<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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜