1

I'm having much difficulty trying to resolve an issue I am facing. We have a source XML Schema that we are using XSLT to transform to a Target Schema. However one of the elements in the Target Schema is designed to hold the raw XML from the source XML (including atributes). I do not wish to use CDATA as that will cause issues when the data is being consumed again. I'm running this XSLT in BizTalk 2009 so I will be limited only to using XSLT 1.0/XPATH 1.0.

Oh, and to complicate things just a little further, the data in the source XML has < and > in some of the elements.

Source Example:

<root>
<foo company="1">
    <bar id="125" title="foobar3">
    &gt; 15 years
</bar>
    <bar id="126" title="foobar4">
    &lt; 5 years
</bar>
</foo>
<foo company="2">
    <bar id="125" title="foobar3">
    &gt; 15 years
</bar>
    <bar id="126" title="foobar4">
    &lt; 5 years
</bar>
</foo>

Example Target

<newXML>
    <Company>1</Company>
    <SourceXML>
&lt;root&gt;
    &lt;foo company="1"&gt;
        &lt;bar id="125" title="foobar3"&gt;
        "&gt;" 15 years
    &lt;/bar&gt;
        &lt;bar id="126" title="foobar4"&gt;
        "&lt;" 5 years
    &lt;/bar&gt;
    &lt;/foo&gt;
    &lt;foo company="2"&gt;
        &lt;bar id="125" title="foobar3"&gt;
        "&gt;" 15 years
    &lt;/bar&gt;
        &lt;bar id="126" title="foobar4"&gt;
        "&lt;" 5 years
    &lt;/bar&gt;
    &lt;/foo&gt;
&lt;/root&gt;   
    </SourceXML>
</newXML>
TimWagaman
  • 980
  • 1
  • 10
  • 31
  • Even without using CDATA sections, the text into which the XML document is demoted is almost completely unusable. What is extremely bad here is to destroy the markup by converting it to string. A correct design will just copy the wanted tree -- not smash it into unusable text. – Dimitre Novatchev May 10 '12 at 03:21
  • The purpose of smashing it into unusable text is to send the XML to SQL Server via a WCF-SQL adapter. The source XML is being stored in the database for future usage. – TimWagaman May 10 '12 at 13:52
  • Then just *copy* the wanted subtree and place the result of the transformation in SQL Server. You can find the appropriate MSDN documentation how to get the result of the transformation as an in-memory string. – Dimitre Novatchev May 10 '12 at 15:27

1 Answers1

1

It's not that complicated. You just have to write two templates for element and attribute nodes that write the desired text representation to the output tree. Only the wrapping of < and > with quotation marks in text nodes requires a bit more typing. The stylesheet could look like this one:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/">
      <newXML>
         <Company>1</Company>
         <SourceXML>
            <xsl:apply-templates/>
         </SourceXML>
      </newXML>
   </xsl:template>

   <xsl:template match="*">
      <xsl:value-of select="concat('&lt;',name())"/>
      <xsl:apply-templates select="@*"/>
      <xsl:text>&gt;</xsl:text>
      <xsl:apply-templates select="node()"/>
      <xsl:value-of select="concat('&lt;/',name(), '>')"/>
   </xsl:template>

   <xsl:template match="@*">
      <xsl:value-of select="concat(' ', name(),'=','&quot;', ., '&quot;')"/>
   </xsl:template>

   <xsl:template match="text()">
      <xsl:call-template name="wrap">
         <xsl:with-param name="str">
            <xsl:call-template name="wrap">
               <xsl:with-param name="str" select="."/>
               <xsl:with-param name="find" select="'&lt;'"/>
            </xsl:call-template>
         </xsl:with-param>
         <xsl:with-param name="find" select="'&gt;'"/>
      </xsl:call-template>
   </xsl:template>

   <xsl:template name="wrap">
      <xsl:param name="str"/>
      <xsl:param name="find"/>
      <xsl:choose>
         <xsl:when test="contains($str, $find)">
            <xsl:variable name="before" select="substring-before($str, $find)"/>
            <xsl:variable name="after" select="substring-after($str, $find)"/>
            <xsl:value-of select="concat($before, '&quot;', $find, '&quot;')"/>
            <xsl:call-template name="wrap">
               <xsl:with-param name="str" select="$after"/>
               <xsl:with-param name="find" select="$find"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:value-of select="$str"/>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>
Martin
  • 1,748
  • 1
  • 16
  • 15