简体   繁体   中英

Replace value in a node retaining the XML Structure using XSLT

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>

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>

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM