Группировка и удаление дубликатов - PullRequest
0 голосов
/ 22 января 2010

С учетом следующего 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"/>

Я пытаюсь создать следующий вывод:

<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>

Я создал следующую функцию для разбиения строки:

<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>

Я нашел ссылки на использование элемента xsl: key, но я не видел очевидного способа использовать его в контексте. Любая идея? (Я использую xsltproc (XSLT1.0) для преобразования.)

Ответы [ 2 ]

2 голосов
/ 22 января 2010

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>

применительно к

<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>

Результаты в

<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>

Выражение XPath заменяет ваш шаблон getPhysicalInterfaceName.

Это так, на примерах 'Serial2/0.0' и 'Serial3/0:1':

  1. добавьте ':' (=> 'Serial2/0.0:'; 'Serial3/0:1:')
  2. принять все до первого ':' (=> 'Serial2/0.0'; 'Serial3/0')
  3. добавьте '.' (=> 'Serial2/0.0.'; 'Serial3/0.')
  4. принять все до первого '.' (=> 'Serial2/0'; 'Serial3/0')

РЕДАКТИРОВАТЬ: Упрощенное выражение XPath. Моя первая попытка сработала, но была более сложной:

concat(
  substring-before(@name, '/'), 
  '/',
  substring-before(
    concat(
      translate(substring-after(@name, '/'), '.', ':'), ':'
    ),
    ':'
  )
)

На положительной стороне вышеприведенное выражение правильно обрабатывает двоеточия и точки в первой части имени, например, 'Some.Serial3/0:1'. Короче нет. Если вы ожидаете точки в имени, используйте более длинное выражение. Объяснение этому есть в истории изменений этого поста .

1 голос
/ 22 января 2010

Вам следует взглянуть на метод Мюнхена для группировки элементов XML.

...