[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Selecting (and excluding) nodes


Subject: Selecting (and excluding) nodes
From: Marcus Andersson <marcus@xxxxxxxxxx>
Date: Tue, 22 Jun 2004 16:55:58 +0200

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) &lt;= $ip and $ip &lt;= 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[@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[@ref=current()/@ref]">
            <item ref="{@ref}"/>
          </xsl:if>
        </xsl:for-each>
      </category>
    </xsl:variable>
    <xsl:variable name="cat" select="msxsl:node-set($acat)/category"/>
    <xsl:if test="count($cat/*) &gt; 0">
      <xsl:copy-of select="$cat"/>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>


Any help is appreciated (optimizing as well)


/Marcus


Current Thread
Keywords
xml