0

I have a requirement where I need to replace the value of a node based on the attributes with a different value, retaining the original XML structure. Basically, I need to copy the whole XML only by replacing certain values when specific attributes are encountered in the original XML.

Below is a sample XML before transformation and after transformation.

Original XML:

<root>
    <body>
        <node_level1>
            <node_level2>
                <node_level3>
                    <value animal="cat">Munchkin</value>
                </node_level3>
                <node_level3>
                    <value animal="cat">Turkish Angora</value>
                </node_level3>
                <node_level3>
                    <value animal="cat">La Perm</value>
                </node_level3>
            </node_level2>
            <node_level2>
                <node_level3>
                    <node_level4>
                        <value animal="dog">Siberian Husky</value>
                    </node_level4>
                    <node_level4>
                        <value animal="dog">Pug</value>
                    </node_level4>
                    <node_level4>
                        <value animal="dog">Beagle</value>
                    </node_level4>
                </node_level3>
            </node_level2>
        </node_level1>
    </body>
</root>

After transforming the data using XSLT, I need the XML as below:

<root>
    <body>
        <node_level1>
            <node_level2>
                <node_level3>
                    <value animal="cat">Cat Family</value>
                </node_level3>
                <node_level3>
                    <value animal="cat">Cat Family</value>
                </node_level3>
                <node_level3>
                    <value animal="cat">Cat Family</value>
                </node_level3>
            </node_level2>
            <node_level2>
                <node_level3>
                    <node_level4>
                        <value animal="dog">Dog Family</value>
                    </node_level4>
                    <node_level4>
                        <value animal="dog">Dog Family</value>
                    </node_level4>
                    <node_level4>
                        <value animal="dog">Dog Family</value>
                    </node_level4>
                </node_level3>
            </node_level2>
        </node_level1>
    </body>
</root>

Here is the code that works for specified attributes, I need to generalize it.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl" version="2.0">
    <xsl:template match="/">
        <root>
            <xsl:for-each select="//*[value[@animal = 'cat']]">
                <xsl:copy>
                    <xsl:value-of select="@*"/>
                    <xsl:value-of select="'Cat Family'"/>
                </xsl:copy>
            </xsl:for-each>
        </root>
    </xsl:template>
</xsl:stylesheet>
3
  • So what have you tried? This is a good task to write your first stylesheet, just start with the identity transformation and then set up the template matching the nodes you want to change and in the template body create the new content you want Commented Feb 20, 2020 at 11:04
  • Here is the code that works for specified attributes, I need to generalize it. ` <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl" version="2.0"> <xsl:template match="/"> <root> <xsl:for-each select="//*[value[@animal = 'cat']]"> <xsl:copy> <xsl:value-of select="@*"/> <xsl:value-of select="'Cat Family'"/> </xsl:copy> </xsl:for-each> </root> </xsl:template> </xsl:stylesheet>` Commented Feb 20, 2020 at 11:45
  • It is fine that you want to show us your code, but please edit your question and insert it there as a well-formatted code sample so that we have a chance to read it. And as I said, the identity transformation (e.g. declared in XSLT 3 as <xsl:mode on-no-match="shallow-copy"/>) plus a template matching the nodes you want to transform is the right approach. Commented Feb 20, 2020 at 11:58

2 Answers 2

3

This is very simple task you need to use identity template to copy of all xml nodes as it is:

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

Then you can use a seperate template of value element as per your requirement:

<xsl:template match="value">
    <xsl:choose>
        <xsl:when test="@animal = 'cat'">
            <value animal="cat">Cat Family</value>
        </xsl:when>
        <xsl:when test="@animal = 'dog'">
            <value animal="dog">Dog Family</value>
        </xsl:when>
    </xsl:choose>
</xsl:template>

Final XSL file is :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl" version="2.0">

    <!-- indent elements to see properly -->
    <xsl:output indent="yes"/>

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

    <xsl:template match="value">
        <xsl:choose>
            <xsl:when test="@animal = 'cat'">
                <value animal="cat">Cat Family</value>
            </xsl:when>
            <xsl:when test="@animal = 'dog'">
                <value animal="dog">Dog Family</value>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that helps. I will do similarly in my original code.
1

In XSLT 3 you can declare the identity transformation with an xsl:mode on-no-match="shallow-copy" as the default processing and then match on value[@animal]:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="value[@animal]">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:value-of select="@animal || ' family'"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/94Acsmj

Comments

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.