Grouping and removing duplicate items
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开发者_Go百科 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.)
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'
:
- append a
':'
(=>'Serial2/0.0:'
;'Serial3/0:1:'
) - take everything before the first
':'
(=>'Serial2/0.0'
;'Serial3/0'
) - append a
'.'
(=>'Serial2/0.0.'
;'Serial3/0.'
) - take everything before the first
'.'
(=>'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.
You should to take a look into Muenchian method to group XML items.
精彩评论