0
votes

I have a xml like follows,

<doc>
    <a type="atr111"></a>
    <a type="atr111"></a>
    <a type="atr111"></a>
    <a type="atr222"></a>
    <a type="atr222"></a>
    <a type="atr222"></a>
</doc>

my requirements are,

  1. add dynamically increment id attribute to nodes which has attribute atr111 and atr222
  2. add new node inside nodes, named <newNode> having attribute id="newAttr" which has attribute atr111 and atr222
  3. change <a> nodes attribute value atr111 to atr222.

so my expected output is,

<doc>
    <a id="id-1" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-2" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-3" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-4" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-5" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-6" type="atr222"><newNode id="newAttr"/></a>
</doc>

the xsl I have written to get those output is follows,

<xsl:template match="a" priority="1">
        <!-- add new dynamic id -->
      <xsl:copy>
            <xsl:attribute name="id">
                <xsl:value-of select="'id-'"/> 
                <xsl:number count="a[@type='atr111' or @type='atr222']" level="any"/>
            </xsl:attribute>
        </xsl:copy>

        <!-- add newNode inside <a> node -->
        <xsl:copy>
        <newNode>
            <xsl:attribute name="id">newAttr</xsl:attribute>
        </newNode>
      </xsl:copy>
    </xsl:template>

    <!-- change existing 'atr111' attribute value to 'atr222' -->
    <xsl:template match="a/@type[. = 'atr111']">
        <xsl:attribute name="type">atr222</xsl:attribute>
    </xsl:template>  

My current output comes as follows,

<doc>
    <a id="id-1"/><a><newNode id="newAttr"/></a>
    <a id="id-2"/><a><newNode id="newAttr"/></a>
    <a id="id-3"/><a><newNode id="newAttr"/></a>
    <a id="id-4"/><a><newNode id="newAttr"/></a>
    <a id="id-5"/><a><newNode id="newAttr"/></a>
    <a id="id-6"/><a><newNode id="newAttr"/></a>
</doc>

As you can see dynamic ids have added as expected, and <newNode> with new attribute has also added.but it has duplicated the <a> node. also existing attribute type has disappeared.

How can I organize my code to get the expected output?

1

1 Answers

1
votes

You forgot to copy the type attribute. <xsl:copy> only copies the current node itself, not its children or attributes.

The following utilizes the identity template for the process of copying children and attributes.

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" />

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

    <xsl:template match="a[@type='atr111' or @type='atr222']">
        <xsl:copy>
            <xsl:attribute name="id">
                <xsl:text>id-</xsl:text>
                <xsl:number count="a[@type='atr111' or @type='atr222']" level="any" />
            </xsl:attribute>
            <xsl:apply-templates select="@*|node()" />
            <newNode id="newAttr" />
        </xsl:copy>
    </xsl:template>
</xsl:transform>

Output:

<doc>
    <a id="id-1" type="atr111"><newNode id="newAttr"/></a>
    <a id="id-2" type="atr111"><newNode id="newAttr"/></a>
    <a id="id-3" type="atr111"><newNode id="newAttr"/></a>
    <a id="id-4" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-5" type="atr222"><newNode id="newAttr"/></a>
    <a id="id-6" type="atr222"><newNode id="newAttr"/></a>
</doc>

You could of course also use <xsl:copy-of select="@type" /> if there are no other children, but that's less flexible: Copying through the identity template enables you to carry over variable input and to add, for example, an <xsl:template match="a/@type"> later-on that does some special processing to @type nodes, if necessary.