tags:

views:

129

answers:

2

I have xml like this:

  <configurationData>
    <path name='b'>
      <path name='a'>
        <setting name='s1'>
        ![CDATA[XXXX]]
        </setting>
        <setting name='s2'>
          XXXX
        </setting>
      </path>
    </path>
  </configurationData>

where configurationData is the root node, and there can be may nested paths followed by one or more setting nodes. I want to convert the setting node to put the contents of the setting node into a child node called value

  <configurationData>
    <path name='b'>
      <path name='a'>
        <setting name='s1'>
          <value>![CDATA[XXXX]]</value>
        </setting>
        <setting name='s2'>
          <value>XXXX</value>
        </setting>
      </path>
    </path>
  </configurationData>

I must admit I find XML a mental road block and I cannot see what XSLT to use:

This is my attempt:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:output encoding ="utf-8" indent="yes" method="xml" version="1.0"/>

  <xsl:template match='/setting'>
    <xsl:apply-templates select='setting' />
  </xsl:template>

  <xsl:template match='setting'>
    <value>
      <xsl:value-of select='.'/>
    </value>
  </xsl:template>
</xsl:stylesheet>
+3  A: 

You are 90% of the way there. What you need is the "identity template"

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

    <xsl:output encoding ="utf-8" indent="yes" method="xml" version="1.0"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match='setting'>
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <value>
                <xsl:value-of select='.'/>
            </value>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

The first template processes all nodes, copying them to the output. However, the 'setting' template, being more specific, gets invoked for 'setting' nodes, That template copies the node itself and its attributes, then wraps the value in a 'value' tag.

The most non-intiutive thing about XSLT is that the stylesheet is not a program which drives the process. Instead, it is the input XML document that controls, with the stylesheet providing instructions that get selected and executed according to what's in the input. This is called "push" processing. The XSL processor pushes data to your stylesheet. XSLT does have some procedural capabilities, and you can write a stylesheet in "pull" style, where the stylesheet attempts to drive the process, but this is harder and leads to hard-to-maintain stylesheets.

Edit: To enable CDATA sections replace:

<xsl:value-of select='.' />

with

![CDATA[<xsl:value-of select='.' disable-output-escaping="yes"/>]]

(though not the best solution as it always puts CDATA in)

Jim Garrison
Fantastic jim. Only one little issue - the CDATA info is transliterated. I need the CDATA preserved as CDATA.
Preet Sangha
Just a thought. I suppose I could just using a CDATA always couldn't I?
Preet Sangha
@Preet Sangha: Yes, by declaring `cdata-section-elements` - see my answer. However, I would not care if it comes out as CDATA or not, since on the "data level" it is equivalent.
Tomalak
@Jim Garrison: +1. The complexity can be reduced some more in the second template by matching `setting/text()` specifically.
Tomalak
@jim: i need to retain this as the information needs to be hand editable as well as machine. The transliteration makes it very hard to maintain the data. But Thank you. I'll look up the cdata-section stuff. Muchos gracias.
Preet Sangha
Hope you don't mind. I've added my own edit for the CDATA stuff.
Preet Sangha
A: 

My suggestion, based on Jim Garrison answer:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="xml" encoding="utf-8" cdata-section-elements="value" />

  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="setting/text()">
    <value>
      <xsl:value-of select="." />
    </value>
  </xsl:template>

</xsl:stylesheet>
Tomalak
thank you very much. could you indicate why this may be better please?
Preet Sangha
It is a little bit less complex and more idiomatic. Other than that, there is not much difference.
Tomalak