tags:

views:

33

answers:

2

Given this XML...

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <item>
        <this>
<that>one</that>
        </this>
    </item>
    <item>
        <this>
<that>two</that>
        </this>
    </item>
    <item>
        <this>
<that>three</that>
        </this>
    </item>
</root>

I want to make copies of the items into a new format which looks like...

<new>
  <parm x="&gt;that&lt;one&gt;/that&lt;"/>
  <parm x="&gt;that&lt;two&gt;/that&lt;"/>
  <parm x="&gt;that&lt;three&gt;/that&lt;"/>
</new>

The style sheet...

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:param name="feedKey"/>

    <xsl:template match="/">
        <new>
            <xsl:apply-templates select="//item"/>
        </new>
    </xsl:template>

    <xsl:template match="item">
        <xsl:element name="param">
            <xsl:attribute name="x"><xsl:copy-of select="node()"/></xsl:attribute>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

...produces...

<?xml version="1.0" encoding="UTF-8"?>
<new>
   <param x="&#xA;        &#xA;one&#xA;        &#xA;    "/>
   <param x="&#xA;        &#xA;two&#xA;        &#xA;    "/>
   <param x="&#xA;        &#xA;three&#xA;        &#xA;    "/>
</new>

A simple change to the sheet to remove the attribute...

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:param name="feedKey"/>

    <xsl:template match="/">
        <new>
            <xsl:apply-templates select="//item"/>
        </new>
    </xsl:template>

    <xsl:template match="item">
        <xsl:element name="param">
            <xsl:copy-of select="node()"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

...produces...

<?xml version="1.0" encoding="UTF-8"?>
<new>
   <param>
        <this>
         <that>one</that>
        </this>
    </param>
   <param>
        <this>
         <that>two</that>
        </this>
    </param>
   <param>
        <this>
         <that>three</that>
        </this>
    </param>
</new>

How can I convert "this" into attribute "x" with the white space stripped and the tags encoded?

A: 

To my knowledge and if I understand your question correctly; there isn't a way of doing this included in XSLT. My assumption is that W3C consider storing escaped XML to be an anti-pattern. My approach was to define a named template:

<xsl:template name='recursive_escape'>
<xsl:text>;&lt;</xsl:text>
<xsl:value-of select='local-name()'/>
<xsl:for-each select='child::attribute()'>
<xsl:value-of select='local-name()'/>
<xsl:text>='</xsl:text><xsl:value-of select='.'/><xsl:text>'</xsl:text>
</xsl:for-each>
<xsl:text>/;&gt;</xsl:text>....

And so on. I hope you can decipher what I'm trying to do here: construct manually an opening tag with the name of the context node and the name and value of each attribute, then iterate through every child node of the context node and call the same template again; and so on. I don't have my original implementation to hand here so no doubt there's a few things wrong with my example; it's just a guide.

If anybody knows of a better way I'd like to hear it; it may be in XSLT 2.0 which isn't formalised yet, IIRC. For robustness and MSXML support, it's going to have to be XSLT 1.0.

Tom W
+1  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <new>
    <xsl:apply-templates select="*/*/this/*"/>
  </new>
 </xsl:template>

 <xsl:template match="*">
  <xsl:variable name="vStr">
    <xsl:text>&lt;</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>&gt;</xsl:text>

    <xsl:apply-templates/>

    <xsl:text>&lt;/</xsl:text>
    <xsl:value-of select="name()"/>
    <xsl:text>&gt;</xsl:text>
  </xsl:variable>

  <parm x="{$vStr}"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <item>
        <this>
            <that>one</that>
        </this>
    </item>
    <item>
        <this>
            <that>two</that>
        </this>
    </item>
    <item>
        <this>
            <that>three</that>
        </this>
    </item>
</root>

produces the desired result:

<new>
    <parm x="&lt;that&gt;one&lt;/that&gt;"/>
    <parm x="&lt;that&gt;two&lt;/that&gt;"/>
    <parm x="&lt;that&gt;three&lt;/that&gt;"/>
</new>
Dimitre Novatchev