Ash
Ash

Reputation: 582

Call XSLT template recursion with key match

I have a xml like this,

<categories>
    <data>
        <category_id>216</category_id>
        <children_sort_order>217</children_sort_order>
        <children_sort_order>324</children_sort_order>
        <children_sort_order>395</children_sort_order>
        <children_sort_order>580</children_sort_order>
        <children_sort_order>506</children_sort_order>
        <children_sort_order>466</children_sort_order>
        <children_sort_order>626</children_sort_order>
        <children_sort_order>532</children_sort_order>
        <depth>0</depth>
        <description/>
        <name>Products</name>
        <path>products</path>
        <slug>products</slug>
        <state>live</state>
        <status>
            <state>live</state>
        </status>
        <updated_at>2016-06-09T05:13:08Z</updated_at>
        <westfield_locale>en_AU</westfield_locale>
        <sort_order>0</sort_order>
    </data>
    <data>
        <category_id>217</category_id>
        <children_sort_order>218</children_sort_order>
        <children_sort_order>319</children_sort_order>
        <children_sort_order>232</children_sort_order>
        <children_sort_order>237</children_sort_order>
        <children_sort_order>244</children_sort_order>
        <children_sort_order>255</children_sort_order>
        <children_sort_order>225</children_sort_order>
        <children_sort_order>307</children_sort_order>
        <children_sort_order>286</children_sort_order>
        <children_sort_order>262</children_sort_order>
        <children_sort_order>269</children_sort_order>
        <children_sort_order>279</children_sort_order>
        <children_sort_order>251</children_sort_order>
        <children_sort_order>299</children_sort_order>
        <children_sort_order>292</children_sort_order>
        <depth>1</depth>
        <description/>
    <name>Women's</name>
        <path>products/womens-fashion-accessories</path>
        <slug>womens-fashion-accessories</slug>
        <state>live</state>
        <status>
            <state>live</state>
        </status>
        <updated_at>2016-07-12T03:35:17Z</updated_at>
        <westfield_locale>en_AU</westfield_locale>
        <sort_order>0</sort_order>
    </data>
    <errors />
    <meta>
        <api_version>1.3</api_version>
        <deprecation_information />
    </meta>
</categories>

I want to read the top level element and generate parent xml element then read the related category and insert as a child to first element. So the output should be like this

<Category category_id="216" name="Products" parent="0">
  <Category category_id="217" name="Women's" parent="216">
  </Category>
</Category> 

I tried to apply recursive template with key matching but that did not work.

<xsl:key name="children-search" match="data" use="category_id"/>
    <xsl:template name="Category">
            <xsl:param name="parent"/>

            <xsl:variable name="current" select="category_id"/>

            <xsl:element name="Category">
                <xsl:attribute name="id"><xsl:value-of select="category_id"/></xsl:attribute>
                <xsl:attribute name="code">1</xsl:attribute><!--<xsl:value-of select="sort_order"/></xsl:attribute>--><!-- Field is no longer provided in the Westfield Category Data -->
                <xsl:attribute name="name"><xsl:value-of select="slug"/></xsl:attribute>
                <xsl:attribute name="displayname"><xsl:value-of select="name"/></xsl:attribute>
                <xsl:attribute name="desc"><xsl:value-of select="description"/></xsl:attribute>
                <xsl:attribute name="ismappable">1</xsl:attribute> <!-- Field is no longer provided in the Westfield Category Data -->
                <xsl:attribute name="order"><xsl:value-of select="sort_order"/></xsl:attribute>
                <xsl:attribute name="active"><xsl:choose><xsl:when test="state = 'live'">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:attribute>
                <xsl:attribute name="parent"><xsl:value-of select="$parent"/></xsl:attribute>
            </xsl:element>

            <!-- recursive call -->
            <xsl:for-each select="children_sort_order">
                <xsl:call-template name="Category"><xsl:with-param name="parent" select="key('children-search', $current)"/></xsl:call-template>
            </xsl:for-each>
        </xsl:template> 

Please let me know if there is better way to approach this. Thanks a lot

Upvotes: 0

Views: 343

Answers (1)

Tim C
Tim C

Reputation: 70638

You don't need named templates with parameters here. Instead, if you have a template that matched data, you could then get the child elements like so:

<xsl:for-each select="children_sort_ordesr">
   <xsl:apply-templates select="key('children-search', .)" />
</xsl:for-each>

Or better still (Thanks to Martin Honnen!) this..

 <xsl:apply-templates select="key('children-search', children_sort_order)" />

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="children-search" match="data" use="category_id"/>
<xsl:key name="parent_search" match="children_sort_order" use="."/>

 <xsl:template match="categories">
    <xsl:copy>
       <xsl:apply-templates select="data[not(key('parent_search', category_id))]" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="data">
     <Category id="{category_id}" name="{name}">
       <!-- recursive call -->
       <xsl:apply-templates select="key('children-search', children_sort_order)" />
     </Category>
   </xsl:template>
</xsl:stylesheet>

Note the use of the "parent_search" key which is simply used to pick the top-level data element (i.e. the data element which has no parent). You could perhaps simplify it to <xsl:apply-templates select="data[1]" /> if the top level data element was always the first data element in the XML.

Also note the use of Attribute Value Templates to create the attributes on the Category element, which simplifies the XSLT greatly.

Upvotes: 1

Related Questions