Как вывести дубликаты элементов с помощью 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"/>
объяснение:
Muenchian группировка использование составного ключа (на
name
иdisplayName
атрибуты).единственный шаблон в коде соответствует любому
Field
элемент, который является вторым в соответствующей группе. Затем внутри тела шаблона выводится вся группа.Muenchian группировка составляет the эффективный способ группировки в XSLT 1.0. Ключи используются для эффективности.
см. Также мой ответ этот вопрос.
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"/>
объяснение:
использование
<xsl:for-each-group>
использование
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>