Как вывести дубликаты элементов с помощью XSLT?

у меня есть XML, который выглядит примерно так -

<Root>
  <Fields>
    <Field name="abc" displayName="aaa" />
    <Field name="pqr" displayName="ppp" />
    <Field name="abc" displayName="aaa" />
    <Field name="xyz" displayName="zzz" />
  </Fields>
</Root>

Я хочу, чтобы выходные данные содержали только те элементы, которые имеют повторяющийся name-displayName комбинация, если есть какие - либо -

<Root>
      <Fields>
        <Field name="abc" displayName="aaa" />
        <Field name="abc" displayName="aaa" />
      </Fields>
</Root>

как я могу это сделать с помощью XSLT?

2 ответов


трансформация:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kFieldByName" match="Field"
  use="concat(@name, '+', @displayName)"/>

 <xsl:template match=
  "Field[generate-id()
        =
         generate-id(key('kFieldByName',
                     concat(@name, '+', @displayName)
                     )[2])
        ]
  ">
     <xsl:copy-of select=
     "key('kFieldByName',concat(@name, '+', @displayName))"/>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному XML-документу:

<Root>
    <Fields>
        <Field name="abc" displayName="aaa" />
        <Field name="pqr" displayName="ppp" />
        <Field name="abc" displayName="aaa" />
        <Field name="xyz" displayName="zzz" />
    </Fields>
</Root>

производит желаемый результат:

<Field name="abc" displayName="aaa"/>
<Field name="abc" displayName="aaa"/>

объяснение:

  1. Muenchian группировка использование составного ключа (на name и displayName атрибуты).

  2. единственный шаблон в коде соответствует любому Field элемент, который является вторым в соответствующей группе. Затем внутри тела шаблона выводится вся группа.

  3. Muenchian группировка составляет the эффективный способ группировки в XSLT 1.0. Ключи используются для эффективности.

  4. см. Также мой ответ этот вопрос.

II. XSLT 2.0 решение:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:for-each-group select="/*/*/Field"
          group-by="concat(@name, '+', @displayName)">
       <xsl:sequence select="current-group()[current-group()[2]]"/>
   </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

когда это преобразование применяется к предоставленному XML-документу (показано выше), снова получается нужный, правильный результат:

<Field name="abc" displayName="aaa"/>
<Field name="abc" displayName="aaa"/>

объяснение:

  1. использование <xsl:for-each-group>

  2. использование current-group()


чтобы найти дубликаты, вам нужно повторить Field элементы и для каждого из них искать набор Field элементы во всем документе, которые имеют соответствие name и displayName значения атрибутов. Если набор содержит более 1 элемента, этот элемент добавляется в выходные данные.

вот пример шаблона, который достигает этого:

<xsl:template match="Field">
    <xsl:variable name="fieldName" select="@name" />
    <xsl:variable name="fieldDisplayName" select="@displayName" />
    <xsl:if test="count(//Field[@name=$fieldName and @displayName=$fieldDisplayName]) > 1">
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template>

выполнение этого шаблона (завернутого в соответствующий файл XSLT) на ваших образцах данных дает следующее вывод:

<?xml version="1.0" encoding="utf-8"?>
<Root>
  <Fields>
    <Field name="abc" displayName="aaa" />
    <Field name="abc" displayName="aaa" />
  </Fields>
</Root>