Havik23
Havik23

Reputation: 75

XSLT copy & setting attribute values

Below is my my starting XML:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:something-well-formed"> 
  <child1 attr1="a" attr2="b"> 
     <child1 attr1="c" attr2="b"/>
  </child1>
  <child3/>
  <child1 attr1="d" attr2="b"> 
     <child2 attr1="e" attr2="b"/> 
  </child1>
</root>

After running a transform on the above, I get an intermediary resulting xml as such with all the attributes stripped, in this case, from child1 nodes:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:something-well-formed"> 
   <child1> 
      <child1/>
   </child1>
   <child3/>
   <child1> 
      <child2 attr1="e" attr2="b"/> 
   </child1>
</root>

What I would like to do would be to run a transform on the intermediate result produced above to create an xml doc that would look like the below example, where I could specify the nth instance of, in this case, child1 and set it's attributes accordingly:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:something-well-formed"> 
   <child1  attr1="something", attr2="something else"> 
      <child1/>
   </child1>
   <child3/>
   <child1> 
      <child2 attr1="e" attr2="b"/> 
   </child1>
</root>

This is the example xslt I've attempted to use:

<xsl:param name="element" />
    <xsl:param name="attributes" />
    <xsl:param name="nodeNumber"/>

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

    <xsl:template match="*[name(.)=$element]">
        <xsl:copy>
            <xsl:apply-templates select="@*" />

            <!-- Splits into separate key/value pairs elements -->
            <xsl:variable name="attributesSeq" select="tokenize($attributes, ';')" />
            <xsl:for-each select="$attributesSeq">
                <xsl:variable name="attributesSeq" select="tokenize(., ',')" />

                <xsl:variable name="key"
                    select="replace($attributesSeq[1], '&quot;', '')" />
                <xsl:variable name="value"
                    select="replace($attributesSeq[2], '&quot;', '')" />

                <xsl:attribute name="{$key}">
                    <xsl:value-of select="$value" />
                </xsl:attribute>
            </xsl:for-each>

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

</xsl:stylesheet>

The issue is the above xslt copies over the stuff in the "attributes" parameter to every instance of child1, when my goal would be to copy the contents of the "attributes" parameter to the nth instance of child1.

Additional issue: I would like the parametrize the node name to be the nth instance of any node name I chose to pass in. The example above uses child1, but it should work with any node name (i.e child3, child37, etc).

Thanks for the help!

Upvotes: 1

Views: 568

Answers (2)

helderdarocha
helderdarocha

Reputation: 23637

Assuming you receive the number in this <xsl:param>:

<xsl:param name="nodeNumber" select='2'/>

You can add a predicate to your template passing the variable as the argument:

<xsl:template match="*[name(.)=$element][$nodeNumber]">
   ....
</xsl:template>

This will restrict the element based on its position in the context where it is called.

You also have a parameter for the name of the element. If you use:

<xsl:param name="element" select="'child1'" />

It will add attributes on child1. If you replace it with 'child3' or 'child2' it will replace them accordingly. (Obviously for child3 and child2 in your example $nodeNumber has to be 1, since there is only one of each.)

Upvotes: 1

Marcus Rickert
Marcus Rickert

Reputation: 4238

Hmmm. If you restrict your "child" to be the N'th child of the root node whose name is given by element you can use exactly your approach but you will have to replace

<xsl:template match="*[name() = $element]">

by

<xsl:template match="*[name() = $element][$nodeNumber]">

This worked for me (although xsltproc did not accept the syntax, I had to use Saxon instead). If you, however, you would like to target the N'th descendant instance of the child whose name is given by element it will not work. This will be more tricky. I haven't found an answer to that yet.

Upvotes: 2

Related Questions