Reputation: 43
I have a scenario where I need to count the xml tag values irrespective of its tag name. Below is my xml,
<?xml version="1.0" encoding="UTF-8"?>
<output>
<result>
<value>
<color1>G</color1>
<color2>Y</color2>
</value>
<value>
<color1>Y</color1>
<color2>R</color2>
</value>
</result>
<result>
<value>
<color1>G</color1>
<color2>R</color2>
</value>
<value>
<color1>Y</color1>
<color2>R</color2>
</value>
</result>
</output>
The value inside //output/result should remain and be able to add it in the output. Below is the xslt that I used, but it always gives count as 0.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
<xsl:template match="/">
<output>
<xsl:for-each select="//output/result">
<result>
<green><xsl:value-of select="count(/value[./color[*]='G'])" /></green>
<red><xsl:value-of select="count(/value[./color[*]='R'])" /></red>
<yellow><xsl:value-of select="count(/value[./color[*]='Y'])" /</yellow>
<xsl:value-of select="." />
</result>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
The desired output is as below.
<?xml version="1.0" encoding="UTF-8"?>
<output>
<result>
<GreenCount>1</GreenCount>
<RedCount>1</RedCount>
<YellowCount>2</YellowCount>
<value>
<color1>G</color1>
<color2>Y</color2>
</value>
<value>
<color1>Y</color1>
<color2>R</color2>
</value>
</result>
<result>
<GreenCount>1</GreenCount>
<RedCount>2</RedCount>
<YellowCount>1</YellowCount>
<value>
<color1>G</color1>
<color2>R</color2>
</value>
<value>
<color1>Y</color1>
<color2>R</color2>
</value>
</result>
</output>
Upvotes: 2
Views: 328
Reputation: 86774
You're trying to program XSL as if it were an imperative language. It's not, the input XML is driving the process and the XSL "reacts" as input tags are matched to templates.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="result">
<xsl:copy>
<GreenCount><xsl:value-of select="count(value/*[text() = 'G'])"/> </GreenCount>
<RedCount><xsl:value-of select="count(value/*[text() = 'R'])"/></RedCount>
<YellowCount><xsl:value-of select="count(value/*[text() = 'Y'])"/></YellowCount>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The first template is an "identity transform" that copies input to output unchanged, since you are just adding tags to the input.
Then, for each result
(not XSL for-each, just letting the natural order of processing feed you the result
elements):
result
tagvalue
tags having the desired textresult
tag, recursively reinvoke the process using the identity transform to copy them to the output.This produces the following output:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<result>
<GreenCount>1</GreenCount>
<RedCount>1</RedCount>
<YellowCount>2</YellowCount>
<value>
<color1>G</color1>
<color2>Y</color2>
</value>
<value>
<color1>Y</color1>
<color2>R</color2>
</value>
</result>
<result>
<GreenCount>1</GreenCount>
<RedCount>2</RedCount>
<YellowCount>1</YellowCount>
<value>
<color1>G</color1>
<color2>R</color2>
</value>
<value>
<color1>Y</color1>
<color2>R</color2>
</value>
</result>
</output>
Upvotes: 1