Preet Sangha
Preet Sangha

Reputation: 65496

Adding a node to to another in XSLT

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>

Upvotes: 1

Views: 653

Answers (2)

Jim Garrison
Jim Garrison

Reputation: 86774

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)

Upvotes: 3

Tomalak
Tomalak

Reputation: 338208

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>

Upvotes: 0

Related Questions