4

I'm new to XSLT and need to solve a nasty problem and I've no change to solve it. The following example describes my problem:

<a>
  <b1><![CDATA[<CdtrRefInf><Issr>XXX</Issr></Tp><Ref>123456123]]></b1>
  <b2><![CDATA[193</Ref></CdtrRefInf>]]></b2>
</a>

The expected oucome should be:

<a>
  <b1>123456123193<b1>
</a>

I need to iterate over the elements b1 and b2 and concatenate the content into a variable. Then I need to take the content of the Ref element and put this in the b1 element. The following code concatenate the content of the fields b1 and b2 together. But how to put it in the above format?????

<xsl:template match="/*">
    <xsl:variable name="vMyVars">
        <xsl:apply-templates select="b1 | b2 " mode="vMyVars"/>
    </xsl:variable>
    <xsl:value-of select="substring($vMyVars, -1, string-length($vMyVars))"/>
</xsl:template>

<xsl:template match="*" mode="vMyVars"/>

<xsl:template match="*[normalize-space()]" mode="vMyVars">
    <xsl:value-of select="."/>
    <!--<xsl:text>, </xsl:text>-->
</xsl:template>

Any advise is welcome. Regards Dirk

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
Dirk
  • 121
  • 1
  • 7

1 Answers1

4

You need XPath 3.0 (XSLT 3.0) for this. Just use:

 parse-xml(concat(/*/b1, /*/b2))/*/Ref/text()

You also need to correct the text inside the CDATA sections -- it contains </Tp> -- a closing tag that has no corresponding starting tag. I corrected this to: <Tp/>.

With this correction to the provided XML document, it is now:

<a>
  <b1><![CDATA[<CdtrRefInf><Issr>XXX</Issr></Tp><Ref>123456123]]></b1>
  <b2><![CDATA[193</Ref></CdtrRefInf>]]></b2>
</a>

The evaluation of the above XPath 3.0 expression on the above XML document produces the wanted, correct result:

123456123193

I verified this with BaseX using this XQuery 3.0:

let $top :=
<a>
  <b1><![CDATA[<CdtrRefInf><Issr>XXX</Issr><Tp/><Ref>123456123]]></b1>
  <b2><![CDATA[193</Ref></CdtrRefInf>]]></b2>
</a>

 return
    parse-xml(concat($top/b1, $top/b2))/*/Ref/text()

II. Solution in XSLT 2.0

XSLT 2.0 doesn't provide a simple function or instruction to parse a string into an XML document/fragment.

In Saxon, one may use the saxon:parse() extension function:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:saxon="http://saxon.sf.net/">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
     <xsl:copy-of select="saxon:parse(concat(/*/b1, /*/b2))/*/Ref/text()"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is performed with Saxon 9.1.07 on the provided XML document:

<a>
  <b1><![CDATA[<CdtrRefInf><Issr>XXX</Issr><Tp/><Ref>123456123]]></b1>
  <b2><![CDATA[193</Ref></CdtrRefInf>]]></b2>
</a>

the wanted, correct result is produced:

123456123193
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Thanks for your quick reply, I like the solution but we currently work with xslt 2.0. I need to check if we can using xslt 3.0 in our environmet. – Dirk Jan 13 '13 at 11:47
  • 1
    @Dirk, I updated the answer with an XSLT 2.0 (Saxon) solution. – Dimitre Novatchev Jan 13 '13 at 17:15
  • you made my weekend. I really thought this wasn't possible but the solution is short and sweet. Thank you very much for your help. – Dirk Jan 13 '13 at 19:35