I have XML looking something like the following (it might be large with lots of services and nodes so
performance can be a factor):
<docroot>
<services>
<service id="842" name="service1"/>
<service id="843" name="service2"/>
<service id="844" name="service3"/>
<service id="845" name="service4"/>
</services>
<categories>
<section>
<category name="cat1">
<service ref="842"/>
<service ref="844"/>
</category>
<category name="cat2">
<service ref="842"/>
<service ref="843"/>
<service ref="845"/>
</category>
<category name="cat3">
<service ref="843"/>
</category>
</section>
</categories>
<node>
<node>
<services>
<service ref="842"/>
<service ref="844"/>
<service ref="845"/>
</services>
<node>
<ipranges>
<range from="111111111111" to="222222222222"/>
<range from="333333333333" to="444444444444"/>
</ipranges>
<services>
<service ref="843"/>
</service>
</node>
</node>
</nodes>
</docroot>
I'm going to pass an IP into the stylesheet and do a node (on elements named node) lookup based on that. If
the ip passed in is in none of the ranges it will default to the top node element. The thing I then want to
render is two-fold.
1) I want to render all services that is referenced by all <service ref="..."/> in the <node/> found with the
IP and all <service/> elements in all parent <node/> elements. This is easy with ancestor-or-self and some loop.
2) I want to render the categories and and their <service/> elements where all <service/> elements not
selected in 1) is excluded. If a category ends up with no service element it should be totally left out.
An ip that is outside of the ranges specified should render a document similar to the following (structure
wise anyway):
<result>
<items>
<item id="842" name="service1"/>
<item id="843" name="service2"/>
<item id="844" name="service3"/>
<item id="845" name="service4"/>
</items>
<categories>
<category name="cat1">
<item ref="842"/>
<item ref="844"/>
</category>
<category name="cat2">
<item ref="842"/>
<item ref="845"/>
</category>
</categories>
</result>
I currently have the following but it uses node-set() in the category template and I don't want that (it will
force me to do multiple loops as well when I want the "real" output). Is there another solution?
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:param name="ip_param" select="'555555555555'"/>
<xsl:variable name="ip" select="number($ip_param)"/>
<!-- The following three variables might replacable by just one.
I haven't figured out how though.
It's only $selectedNode that is interesting -->
<xsl:variable name="selectedNodeByIp"
select="/docroot/nodes//node[ipranges/range[number(@from) <= $ip and $ip <=
number(@to)]][1]"/>
<xsl:variable name="selectedNodeId">
<xsl:choose>
<xsl:when test="$selectedNodeByIp"><xsl:value-of
select="$selectedNodeByIp/@id"/></xsl:when>
<xsl:otherwise><xsl:value-of
select="/docroot/nodes/node[1]/@id"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="selectedNode"
select="/docroot/nodes//node[(_at_)id=$selectedNodeId]"/>
<xsl:variable name="selectedServices"
select="$selectedNode/ancestor-or-self::node/services/service"/>
<xsl:template match="/docroot">
<docroot>
<items>
<xsl:for-each select="$selectedServices">
<item>
<xsl:copy-of select="@*"/>
</item>
</xsl:for-each>
</items>
<categories>
<xsl:apply-templates select="categories/section/category"/>
</categories>
</docroot>
</xsl:template>
<xsl:template match="category">
<xsl:variable name="acat">
<category>
<xsl:copy-of select="@*"/>
<xsl:for-each select="service">
<xsl:if test="$selectedServices[(_at_)ref=current()/@ref]">
<item ref="{(_at_)ref}"/>
</xsl:if>
</xsl:for-each>
</category>
</xsl:variable>
<xsl:variable name="cat" select="msxsl:node-set($acat)/category"/>
<xsl:if test="count($cat/*) > 0">
<xsl:copy-of select="$cat"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Any help is appreciated (optimizing as well)
/Marcus