Я пытаюсь выяснить, как избежать дубликатов в наборе результатов при применении преобразования XSLT (я использую XSLT 1.0)
Вот источник XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="1.xsl"?>
<root>
<item>
<code>AA</code>
<included-code>XX</included-code>
<included-code>YY</included-code>
<included-code>WW</included-code>
</item>
<item>
<code>BB</code>
<included-code>ZZ</included-code>
<included-code>XX</included-code>
<included-code>YY</included-code>
</item>
<item>
<code>CC</code>
<included-code>VV</included-code>
<included-code>XX</included-code>
<included-code>WW</included-code>
</item>
</root>
Воттаблица стилей:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="item">
<new-item>
<code><xsl:value-of select="code"/></code>
<xsl:variable name="main_code"><xsl:value-of select="code"/></xsl:variable>
<xsl:for-each select="included-code">
<xsl:variable name="current_code"><xsl:value-of select="text()"/></xsl:variable>
<included-code><xsl:value-of select="$current_code"/></included-code>
<xsl:for-each select="/root/item[included-code=$current_code and code!=$main_code]">
<included-code><xsl:value-of select="code"/></included-code>
</xsl:for-each>
</xsl:for-each>
</new-item>
</xsl:template>
</xsl:stylesheet>
Вот результат:
<?xml version="1.0" encoding="UTF-8"?><result>
<new-item>
<code>AA</code>
<included-code>XX</included-code>
<included-code>BB</included-code>
<included-code>CC</included-code>
<included-code>YY</included-code>
<included-code>BB</included-code>
<included-code>WW</included-code>
<included-code>CC</included-code>
</new-item>
<new-item>
<code>BB</code>
<included-code>ZZ</included-code>
<included-code>XX</included-code>
<included-code>AA</included-code>
<included-code>CC</included-code>
<included-code>YY</included-code>
<included-code>AA</included-code>
</new-item>
<new-item>
<code>CC</code>
<included-code>VV</included-code>
<included-code>XX</included-code>
<included-code>AA</included-code>
<included-code>BB</included-code>
<included-code>WW</included-code>
<included-code>AA</included-code>
</new-item>
</result>
Вопрос - как избежать дублирования значений в результате. То есть вот что ожидалось:
<?xml version="1.0" encoding="UTF-8"?><result>
<new-item>
<code>AA</code>
<included-code>XX</included-code>
<included-code>BB</included-code>
<included-code>CC</included-code>
<included-code>YY</included-code>
<included-code>WW</included-code>
</new-item>
<new-item>
<code>BB</code>
<included-code>ZZ</included-code>
<included-code>XX</included-code>
<included-code>AA</included-code>
<included-code>CC</included-code>
<included-code>YY</included-code>
</new-item>
<new-item>
<code>CC</code>
<included-code>VV</included-code>
<included-code>XX</included-code>
<included-code>AA</included-code>
<included-code>BB</included-code>
<included-code>WW</included-code>
</new-item>
</result>
Спасибо, LarsH! Похоже, что следующий скрипт делает свое дело: (я относительно новичок в XSLT, поэтому не уверен, есть ли более элегантный способ сохранить список уже выведенных значений)
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="item">
<new-item>
<code><xsl:value-of select="code"/></code>
<xsl:variable name="main_code"><xsl:value-of select="code"/></xsl:variable>
<xsl:call-template name="processIncludedCodes">
<xsl:with-param name="main_code" select="./code"/>
<xsl:with-param name="codes" select="./included-code"/>
</xsl:call-template>
</new-item>
</xsl:template>
<xsl:template name="processIncludedCodes">
<xsl:param name="main_code"/>
<xsl:param name="codes"/>
<xsl:param name="outputCodes"/>
<xsl:if test="$codes">
<xsl:variable name="current_code"><xsl:value-of select="$codes[1]"/></xsl:variable>
<xsl:variable name="outputCode" select="concat(':', $codes[1], ':')"/>
<xsl:if test="not(contains($outputCodes, $outputCode))">
<included-code><xsl:value-of select="$codes[1]"/></included-code>
</xsl:if>
<xsl:for-each select="/root/item[included-code=$current_code and code!=$main_code]">
<xsl:if test="not(contains($outputCodes, ./code))">
<included-code><xsl:value-of select="code"/></included-code>
</xsl:if>
</xsl:for-each>
<xsl:variable name="outputCodes2">
<xsl:for-each select="/root/item[included-code=$current_code and code!=$main_code]">
<xsl:value-of select="concat(':', code, ':')"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="newOutputCodes" select="concat($outputCodes, $outputCode, $outputCodes2)"/>
<xsl:call-template name="processIncludedCodes">
<xsl:with-param name="main_code" select="$main_code"/>
<xsl:with-param name="codes" select="$codes[position() > 1]"/>
<xsl:with-param name="outputCodes" select="$newOutputCodes"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>