Reputation: 3011
I have this XML .
<?xml version="1.0" encoding="UTF-8"?>
<Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceschemaLocation="browsenode.xsd">
<Node>
<node-id>1</node-id>
<children count="2">
<id> 2 </id>
<id> 3 </id>
</children>
</Node>
<Node>
<node-id>2</node-id>
<children count="1">
<id> 4 </id>
</children>
</Node>
<Node>
<node-id>3</node-id>
<children count="0">
</children>
</Node>
<Node>
<node-id>4</node-id>
<children count="0">
</children>
</Node>
<Node>
<node-id>5</node-id>
<children count="0">
</children>
</Node>
</Result>
and I want to transform this into
<?xml version="1.0" encoding="UTF-8"?>
<Result xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNameSpaceschemaLocation="browsenode.xsd">
<Node>
<node-id>1</node-id>
<children count="2">
<id> 2 </id>
<id> 3 </id>
</children>
</Node>
<Node>
<node-id>2</node-id>
<children count="1">
<id> 4 </id>
</children>
</Node>
<Node>
<node-id>3</node-id>
<children count="0">
</children>
</Node>
<Node>
<node-id>4</node-id>
<children count="0">
</children>
</Node>
</Result>
i.e... given a node-id i want all the nodes which can be reached from i . The top XML is a flattened out version of a tree .
I tried doing it with this XSLT .
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:template name="root-matcher" match="/">
<Result>
<query>
Query String
</query>
<xsl:call-template name="matcher">
<xsl:with-param name="nodeValue" select="'1'" />
</xsl:call-template>
</Result>
</xsl:template>
<xsl:template name="matcher">
<xsl:param name="nodeValue" />
<xsl:if test="/Result/Node/node-id[text()=$nodeValue]">
<Node>
<browseNodeId>
<xsl:value-of select="/Result/Node/node-id" />
</browseNodeId>
<children>
<xsl:for-each select="/Result/Node/children/id">
<id>
<xsl:value-of select="." />
</id>
</xsl:for-each>
</children>
</Node>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
With this , i could only get to one level . 1.How do i do it recursively ?
I tried adding after the in matcher template but , it would not work .
I am very very new to XSLT . in fact I started it today and am trying to understand. Pardon if the question is naive. Any resources/suggestions are welcome. Kindly advise
Upvotes: 0
Views: 167
Reputation: 9627
Your requested output does not relay match with your xslt.
What I understood that you like to have only Nodes (<Node>
) in output which are reachable via child id form an stat node.
Based on your xlst you may try the this:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:template name="root-matcher" match="/">
<Result >
<query>
Query String
</query>
<xsl:call-template name="matcher">
<xsl:with-param name="nodeValue" select="'1'" />
</xsl:call-template>
</Result>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template name="matcher">
<xsl:param name="nodeValue" />
<xsl:apply-templates select="//Node[node-id=$nodeValue]" mode="matcher" />
</xsl:template>
<xsl:template match="Node" mode="matcher">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
<xsl:for-each select="children/id">
<xsl:apply-templates select="//Node[node-id=normalize-space(current()/.)]" mode="matcher" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I kept the root match and the named template "matcher" even if the are not necessary to get the requested output.
Which will generate the following outout:
<Result>
<query>
Query String
</query>
<Node>
<node-id>1</node-id>
<children count="2">
<id> 2 </id>
<id> 3 </id>
</children>
</Node>
<Node>
<node-id>2</node-id>
<children count="1">
<id> 4 </id>
</children>
</Node>
<Node>
<node-id>4</node-id>
<children count="0">
</children>
</Node>
<Node>
<node-id>3</node-id>
<children count="0">
</children>
</Node>
</Result>
Some explanations:
<xsl:template match="@*|node()">
This is an recursive Identity transform one of most most basic xslt design technique.
Also it is good practice to use apply-templates
favored to for-each
.
<xsl:apply-templates select="//Node[node-id=$nodeValue]" mode="matcher" />
This finds all Node elements in the document which are conform to the condition ([node-id=$nodeValue]
): where the value of node-id is the same as in variable nodeValue.
The use of mode is not necessary here. This make it possible to have templates which match the same node but behave differently depending on the used mode form caller.
<xsl:apply-templates select="//Node[node-id=normalize-space(current()/.)]" mode="matcher" />
This is the recursive call to the Node template for the Node with same id as current child.
Have a look to: w3.org
Upvotes: 2