Reputation: 6154
Given the following XML:
<interface name="Serial1/0"/>
<interface name="Serial2/0.0"/>
<interface name="Serial2/0.1"/>
<interface name="Serial3/0:0"/>
<interface name="Serial3/0:1"/>
I am trying to produce the following output:
<interface name="Serial1/0">
<unit name="Serial1/0"/>
</interface>
<interface name="Serial2/0">
<unit name="Serial2/0.0"/>
<unit name="Serial2/0.1"/>
</interface>
<interface name="Serial3/0">
<unit name="Serial3/0:0"/>
<unit name="Serial3/0:1"/>
</interface>
I have created the following function for splitting the string:
<xsl:template name="getPhysicalInterfaceName">
<xsl:param name="interfaceName"/>
<xsl:choose>
<xsl:when test="contains($interfaceName, ':')">
<xsl:value-of select="substring-before($interfaceName, ':')"/>
</xsl:when>
<xsl:when test="contains($interfaceName, '.')">
<xsl:value-of select="substring-before($interfaceName, '.')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$interfaceName"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I found references to using the xsl:key element, but I didn't see an obvious way to use it in the context. Any idea? (I am using xsltproc (XSLT1.0) to do the transformation.)
Upvotes: 0
Views: 777
Reputation: 338128
XSLT 1.0:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:key
name = "kInterfaceByName"
match = "interface"
use = "
substring-before(concat(
substring-before(concat(@name, ':'), ':'),
'.'), '.')
"
/>
<xsl:template match="i">
<xsl:copy>
<xsl:apply-templates mode="group" select="interface" />
</xsl:copy>
</xsl:template>
<xsl:template match="interface" mode="group">
<xsl:variable name="name" select="
substring-before(concat(
substring-before(concat(@name, ':'), ':'),
'.'), '.')
" />
<xsl:variable name="interfaces" select="key('kInterfaceByName', $name)" />
<!-- Muenchian step -->
<xsl:if test="generate-id()=generate-id($interfaces[1])">
<interface name="{$name}">
<xsl:apply-templates mode="unit" select="$interfaces" />
</interface>
</xsl:if>
</xsl:template>
<xsl:template match="interface" mode="unit">
<unit name="{@name}" />
</xsl:template>
</xsl:stylesheet>
when applied to
<i>
<interface name="Serial1/0"/>
<interface name="Serial2/0.0"/>
<interface name="Serial2/0.1"/>
<interface name="Serial3/0:0"/>
<interface name="Serial3/0:1"/>
</i>
results in
<i>
<interface name="Serial1/0">
<unit name="Serial1/0" />
</interface>
<interface name="Serial2/0">
<unit name="Serial2/0.0" />
<unit name="Serial2/0.1" />
</interface>
<interface name="Serial3/0">
<unit name="Serial3/0:0" />
<unit name="Serial3/0:1" />
</interface>
</i>
The XPath expression replaces your getPhysicalInterfaceName
template.
It does, on the examples of 'Serial2/0.0'
and 'Serial3/0:1'
:
':'
(=> 'Serial2/0.0:'
; 'Serial3/0:1:'
)':'
(=> 'Serial2/0.0'
; 'Serial3/0'
)'.'
(=> 'Serial2/0.0.'
; 'Serial3/0.'
)'.'
(=> 'Serial2/0'
; 'Serial3/0'
)EDIT: Simplified XPath expression. My first try worked but was more complex:
concat(
substring-before(@name, '/'),
'/',
substring-before(
concat(
translate(substring-after(@name, '/'), '.', ':'), ':'
),
':'
)
)
On the plus side, the above expression correctly handles colons and dots in the first part of the name, e.g. 'Some.Serial3/0:1'
. The shorter one does not. If you expect dots in the name, use the longer expression. An explanation of it is in the revision history of this post.
Upvotes: 2
Reputation: 57936
You should to take a look into Muenchian method to group XML items.
Upvotes: 1