Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT: Output text of current node only and normalize whitespace

Tags:

xml

xslt

I have an xml document with the below structure. I'm writing a transformation where I'd like to output the text from node B but ignore the element C and text node "title". Essentially I'd like to extract the text "text goes here" and output it in a new element with all the whitespace normalised. Can anybody help? The below is what I've tried so far.

Input Doc
<A>
  <B>
    <C>title</C>
     text goes here
  </B>
</A>

Required output doc
<d>text goes here</d>


Solution A:
<xsl:template match="B">
  <d>       
    <xsl:copy-of select="./text()"/>                
  </d>
</xsl:template>

Problem: the whitespace between elements is preserved so I get something like this:

<d>

  Text goes here

</d>

I also tried using a value-of statement (<xsl:value-of select="./text()"/>) in the template in solution A but this didn't return any text at all. Is there something wrong with the statement?

I should mention that I have overridden the the default text handling template using the following: <xsl:template match="text()" />

Thanks

like image 225
JimS Avatar asked Dec 20 '25 05:12

JimS


1 Answers

The reason <xsl:value-of select="./text()"/> returned "nothing" is that ./text() will return a node set consisting of all the immediate child text nodes of the current node. The value-of a node set is the string value of its first item, which in this case is the whitespace-only text node between the opening <B> and the opening <C> tags. The same applies to the next-most-obvious normalize-space(text()) because that again converts the node set to a string (the first node value) and then normalizes space in that string. Instead, you need to normalize each child text node individually:

<d>
  <xsl:for-each select="text()">
    <xsl:value-of select="normalize-space(.)"/>
  </xsl:for-each>
</d>

One thing to note about this though is that if you have input like

<A>
  <B>
    <C>title</C>
     text goes here
    <C>subtitle</C>
     more text here
  </B>
</A>

then you will get output of

<d>text goes heremore text here</d>

with no space between the bits either side of the subtitle. If this is a problem you can use a trick like

<d>
  <xsl:for-each select="text()[normalize-space(.)]">
    <xsl:if test="position() &gt; 1"><xsl:text> </xsl:text></xsl:if>
    <xsl:value-of select="normalize-space(.)"/>
  </xsl:for-each>
</d>

to iterate over only those text node children that contain non-whitespace characters, and add a space before all but the first of them.

<d>text goes here more text here</d>
like image 171
Ian Roberts Avatar answered Dec 23 '25 14:12

Ian Roberts



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!