Reputation: 3
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>
Upvotes: 0
Views: 323
Reputation: 167516
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
Upvotes: 1
Reputation: 1816
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>
Upvotes: 3