1

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">

  <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>
0

2 Answers 2

3

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">

    <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)

Sign up to request clarification or add additional context in comments.

6 Comments

Fantastic jim. Only one little issue - the CDATA info is transliterated. I need the CDATA preserved as CDATA.
Just a thought. I suppose I could just using a CDATA always couldn't I?
@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.
@Jim Garrison: +1. The complexity can be reduced some more in the second template by matching setting/text() specifically.
@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.
|
0

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>

2 Comments

thank you very much. could you indicate why this may be better please?
It is a little bit less complex and more idiomatic. Other than that, there is not much difference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.