XSLT фильтр дублирует в результате - PullRequest
1 голос
/ 20 февраля 2012

Я пытаюсь выяснить, как избежать дубликатов в наборе результатов при применении преобразования 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>

1 Ответ

1 голос
/ 21 февраля 2012

Без рекурсии:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/root">
        <root>
            <xsl:apply-templates select="item"/>
        </root>
    </xsl:template>

    <xsl:template match="item">
        <new-item>
            <xsl:copy-of select="code"/>
            <xsl:for-each select="included-code | /root/item[included-code = current()/included-code]/code[. != current()/included-code and . != current()/code]">
                <included-code>
                    <xsl:value-of select="."/>
                </included-code>
            </xsl:for-each>
        </new-item>
    </xsl:template>
</xsl:stylesheet>

Он выбирает любые текущие значения <included-code>, плюс для любого элемента с общим значением <included-code> он выбирает <code>, если он еще небыл включен.

Выход:

<?xml version="1.0" encoding="utf-8"?>
<root>
   <new-item>
      <code>AA</code>
      <included-code>XX</included-code>
      <included-code>YY</included-code>
      <included-code>WW</included-code>
      <included-code>BB</included-code>
      <included-code>CC</included-code>
   </new-item>
   <new-item>
      <code>BB</code>
      <included-code>AA</included-code>
      <included-code>ZZ</included-code>
      <included-code>XX</included-code>
      <included-code>YY</included-code>
      <included-code>CC</included-code>
   </new-item>
   <new-item>
      <code>CC</code>
      <included-code>AA</included-code>
      <included-code>BB</included-code>
      <included-code>VV</included-code>
      <included-code>XX</included-code>
      <included-code>WW</included-code>
   </new-item>
</root>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...