The XML included is a psuedo subset of a much larger file, which is generated from a network management system. Each submap tag contains a collection of devices and essentially other submaps which in-turn can contain a collection of devices and submaps and so on.
The problem is, given a submap level that can be passed as a parameter into the stylesheet. I need to form a unduplicated list of groups ( node grps) contained within the submap nodes at the current context of interest. This includes submaps which are descendants of each of the submaps of the context of interest. Let me get the XML and an example of the target output needed this is getting too wordy already.
XML
Expand|Select|Wrap|Line Numbers
- <?xml version="1.0" encoding="utf-8"?>
- <root>
- <submap name="A">
- <device>
- <devname>rtr001</devname>
- <grp>router</grp>
- </device>
- <submap name="E">
- <device>
- <devname>rtr005</devname>
- <grp>router</grp>
- </device>
- <device>
- <devname>SW0001</devname>
- <grp>switch</grp>
- </device>
- </submap>
- </submap>
- <submap name="B">
- <submap name="F">
- <submap name="G">
- <submap name="H">
- <device>
- <devname>ftp003</devname>
- <grp>ftpserver</grp>
- </device>
- <device>
- <devname>www001</devname>
- <grp>webserver</grp>
- </device>
- </submap>
- <device>
- <devname>rtr007</devname>
- <grp>router</grp>
- </device>
- <device>
- <devname>SW0002</devname>
- <grp>switch</grp>
- </device>
- <device>
- <devname>rtr006</devname>
- <grp>router</grp>
- </device>
- <device>
- <devname>srv001</devname>
- <grp>server</grp>
- </device>
- </submap>
- </submap>
- <device>
- <devname>rtr002</devname>
- <grp>router</grp>
- </device>
- <submap name="I">
- <device>
- <devname>rtr005</devname>
- <grp>router</grp>
- </device>
- <device>
- <devname>hub001</devname>
- <grp>hub</grp>
- </device>
- </submap>
- </submap>
- <submap name="C">
- <submap name="j">
- <device>
- <devname>rtr006</devname>
- <grp>router</grp>
- </device>
- <device>
- <devname>av0001</devname>
- <grp>antivirus</grp>
- </device>
- </submap>
- <device>
- <devname>rtr003</devname>
- <grp>router</grp>
- </device>
- </submap>
- <submap name="D">
- <device>
- <devname>rtr004</devname>
- <grp>router</grp>
- </device>
- <device>
- <devname>pop001</devname>
- <grp>mailserver</grp>
- </device>
- </submap>
- </root>
Groups for submaps in B context.
Submap F - router ftpserver,webserver,switch,server
Submap I – router,hub
What I can get, and what I have tried and failed at
What is possible?
A list of all down stream grps split by submap but containing duplicates.
Expand|Select|Wrap|Line Numbers
- <?xml version="1.0"?>
- <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- <xsl:param name="targetsubmap">B</xsl:param>
- <xsl:template match="/">
- <xsl:apply-templates select="//submap[@name=$targetsubmap]" mode="test"/>
- </xsl:template>
- <xsl:template match="submap" mode="test">
- <xsl:text>
- Groups for submaps in </xsl:text><xsl:value-of select="@name"/><xsl:text> context.
- </xsl:text>
- <xsl:apply-templates select="submap" mode="level">
- </xsl:apply-templates>
- </xsl:template>
- <xsl:template match="submap" mode="level">
- <xsl:text>
- Submap </xsl:text>
- <xsl:value-of select="@name"/><xsl:text> - </xsl:text>
- <xsl:for-each select="descendant::grp">
- <xsl:value-of select="."/>
- <xsl:if test="position() != last()">,</xsl:if>
- </xsl:for-each>
- </xsl:template>
- </xsl:stylesheet>
Output
Groups for submaps in B context.
Submap F - ftpserver,webserver,router,switch,router,server
Submap I - router,hub
Note router is duplicated in submap F list, it should be in submap I list
What also fails.
The Muenchian Method
Expand|Select|Wrap|Line Numbers
- <?xml version="1.0"?>
- <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- <xsl:param name="targetsubmap">B</xsl:param>
- <xsl:key name="kdgroups" match="//device" use="grp"/>
- <xsl:template match="/">
- <xsl:apply-templates select="//submap[@name=$targetsubmap]" mode="test"/>
- </xsl:template>
- <xsl:template match="submap" mode="test">
- <xsl:text>
- Groups for submaps in </xsl:text><xsl:value-of select="@name"/><xsl:text> context.
- </xsl:text>
- <xsl:apply-templates select="submap" mode="level">
- </xsl:apply-templates>
- </xsl:template>
- <xsl:template match="submap" mode="level">
- <xsl:text>
- Submap </xsl:text>
- <xsl:value-of select="@name"/><xsl:text> - </xsl:text>
- <xsl:for-each select="descendant::device[generate-id(.)=generate-id(key('kdgroups',grp))]">
- <xsl:value-of select="grp"/>
- <xsl:if test="position() != last()">,</xsl:if>
- </xsl:for-each>
- </xsl:template>
- </xsl:stylesheet>
Groups for submaps in B context.
Submap F - ftpserver,webserver,server
Submap I - hub
This seems to fail because, once an id is generated(see what happens to the router group) it is no longer available to subsequent submaps so it is not included within the output tree as needed. Adding the context to the key declaration would reduce the amount of elements the key is applied to but would still fail. The router and switch groups would appear in the submap F list but not the submap I list as needed. Also the context is passed in as a parameter that cannot appear in the match clause of the key declaration.
The preceding axis solution as per O’Reiley Cookbook – although probably way to slow on a larger file. I attempted this method just in-case. It fails for the same reason as the Muenchian Method. Can any one explain the trailing comma?
Expand|Select|Wrap|Line Numbers
- <?xml version="1.0"?>
- <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- <xsl:param name="targetsubmap">B</xsl:param>
- <xsl:template match="/">
- <xsl:apply-templates select="//submap[@name=$targetsubmap]" mode="test"/>
- </xsl:template>
- <xsl:template match="submap" mode="test">
- <xsl:text>
- Groups for submaps in </xsl:text><xsl:value-of select="@name"/><xsl:text> context.
- </xsl:text>
- <xsl:apply-templates select="submap" mode="level">
- </xsl:apply-templates>
- </xsl:template>
- <xsl:template match="submap" mode="level">
- <xsl:text>
- Submap </xsl:text>
- <xsl:value-of select="@name"/><xsl:text> - </xsl:text>
- <xsl:for-each select="descendant::grp">
- <xsl:sort select="."/>
- <xsl:if test="not(self::node()=preceding::node())">
- <xsl:value-of select="."/>
- <xsl:if test="position() != last()">,</xsl:if>
- </xsl:if>
- </xsl:for-each>
- </xsl:template>
- </xsl:stylesheet>
Output
Groups for submaps in B context.
Submap F - ftpserver,server,webserver
Submap I - hub,
If anybody finds a solution which requires the source XML to be altered this is ok as I wrote the Interface to the Network Management System so I can change it. I should note that the XML you can see is a massively simplified abstraction to demonstrate the point. Basically if I have to change it I will.
Hopefully thanks for the help, let me know any methods you have tried even if they fail so I can tick them of the list. I’m hoping I have misunderstood Muenchian.