Flash
Flash

Reputation: 3011

XSLT Transformation for this XML

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 .

  1. How do i get that children="2" or children="0" attribute through XSLT ?

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

Answers (1)

hr_117
hr_117

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

Related Questions