Monday, April 11, 2011

XSLT: How to change an attribute value during <xsl:copy>?

I have an XML document, I want one of the attributes to change it's value.

First I copied everything from input to output using:

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

And now I want to change the attribute "type" in any element named "property". But I don't know how to do it. Could someone please help? Thanks!

From stackoverflow
  • You need a template that will match your target attribute, and nothing else.

    <xsl:template match='XPath/@myAttr'>
      <xsl:attribute name='myAttr'>This is the value</xsl:attribute>
    </xsl:template>
    

    This is in addition to the "copy all" you already have (and is actually always present by default in XSLT). Having a more specific match it will be used in preference.

    tomato : I've tried it without the "copy all" part and it only got what was between the tags. None of the tag themselves or the attributes got copied.
    Richard : @coderx: Would need to see a sample, not sure what you mean.
  • Tested on a simple example, works fine:

    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
    <xsl:template match="@type[parent::property]">
      <xsl:attribute name="type">
        <xsl:value-of select="'your value here'"/>
      </xsl:attribute>
    </xsl:template>
    

    Edited to include Tomalak's suggestion.

    Tomalak : An alternative version would be
    Welbog : Agreed. Your way is probably more intuitive as it matches up more logically with what the template is for.
    Tomalak : That's what I wanted to say in the original comment as well, but forgot to actually type it. ;-)
    Richard : @Tomalak: Depends. I would prefer the parent/@type. But this is clearly subjective.
    Dimitre Novatchev : property/@type is better as it is more clear and understandable. Probably even more efficient (by several microseconds :) )
  • For the following XML:

    <?xml version="1.0" encoding="utf-8"?>
    <root>
        <property type="foo"/>
        <node id="1"/>
        <property type="bar">
         <sub-property/>
        </property>
    </root>
    

    I was able to get it to work with the following XSLT:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="@*|node()">
         <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>
        </xsl:template>
        <xsl:template match="//property">
         <xsl:copy>
          <xsl:attribute name="type">
           <xsl:value-of select="@type"/>
           <xsl:text>-added</xsl:text>
          </xsl:attribute>
          <xsl:copy-of select="child::*"/>
         </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    
  • This problem has a classical solution: Using and overriding the identity template is one of the most fundamental and powerful XSLT design patterns:

    <xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <!--                                                     -->    
        <xsl:param name="pNewType" select="'myNewType'"/>
    <!--                                                     -->    
        <xsl:template match="node()|@*">
         <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
        </xsl:template>
    <!--                                                     -->    
        <xsl:template match="property/@type">
         <xsl:attribute name="type">
          <xsl:value-of select="$pNewType"/>
         </xsl:attribute>
        </xsl:template>
    </xsl:stylesheet>
    

    When applied on this XML document:

    <t>
      <property>value1</property>
      <property type="old">value2</property>
    </t>
    

    the wanted result is produced:

    <t>
      <property>value1</property>
      <property type="myNewType">value2</property>
    </t>
    
  • I had a similar case where I wanted to delete one attribute from a simple node, and couldn't figure out what axis would let me read the attribute name. In the end, all I had to do was use

    @*[name(.)!='AttributeNameToDelete']

  • Hello

    I have below XSLT which copies XML form source to destination.

    <xsl:param name="seq" select="position()"/>
     <xsl:template match="@*|node()"> 
    

    But position() is assigning only one value to all nodes in the output, Can some one please tell me how to generate sequence in the Rollnumber should increase with a value of 1 for every node

0 comments:

Post a Comment