如何根据多个属性的值消除重复节点?

时间:2011-01-06 09:04:55

标签: xslt xslt-1.0

如何根据多个(超过1个)属性的值消除重复节点?此外,属性名称作为参数传递给样式表。现在我知道使用<xsl:key>元素的Muenchian分组方法。但我发现XSLT 1.0不允许<xsl:key>中的参数/变量。

是否有其他方法可以实现重复节点删除?如果没有Munechian方法那么有效,那就好了。

从previus question更新:

  

XML:

<data id = "root">
  <record id="1" operator1='xxx' operator2='yyy' operator3='zzz'/>
  <record id="2" operator1='abc' operator2='yyy' operator3='zzz'/>
  <record id="3" operator1='abc' operator2='yyy' operator3='zzz'/>
  <record id="4" operator1='xxx' operator2='yyy' operator3='zzz'/>
  <record id="5" operator1='xxx' operator2='lkj' operator3='tyu'/>
  <record id="6" operator1='xxx' operator2='yyy' operator3='zzz'/>
  <record id="7" operator1='abc' operator2='yyy' operator3='zzz'/>
  <record id="8" operator1='abc' operator2='yyy' operator3='zzz'/>
  <record id="9" operator1='xxx' operator2='yyy' operator3='zzz'/>
  <record id="10" operator1='rrr' operator2='yyy' operator3='zzz'/>
</data>

3 个答案:

答案 0 :(得分:3)

分两步进行单一转换的其他方法:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">
    <xsl:key name="kItemByLocal" match="record[@local-key]" use="@local-key"/>
    <xsl:param name="pAttNames" select="'operator1 operator2 operator3'"/>
    <xsl:template match="/">
        <xsl:variable name="vFirstRTF">
            <xsl:apply-templates/>
        </xsl:variable>
        <xsl:apply-templates select="msxsl:node-set($vFirstRTF)/node()"/>
    </xsl:template>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="record[not(@local-key)]">
        <xsl:copy>
            <xsl:attribute name="local-key">
                <xsl:call-template name="local-key"/>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="record[@local-key]
                               [count(.|key('kItemByLocal',@local-key)[1])
                                 != 1]|@local-key"/>
    <xsl:template name="local-key">
        <xsl:param name="pAttributes" select="concat($pAttNames,' ')"/>
        <xsl:if test="normalize-space($pAttributes)">
            <xsl:variable name="vName"
                          select="substring-before($pAttributes,' ')"/>
            <xsl:variable name="vAttribute" select="@*[name()=$vName]"/>
            <xsl:value-of select="concat($vName,'+',$vAttribute,'+')"/>
            <xsl:call-template name="local-key">
                <xsl:with-param name="pAttributes"
                                select="substring-after($pAttributes,' ')"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

输出:

<data id="root">
    <record id="1" operator1="xxx" operator2="yyy" operator3="zzz"></record>
    <record id="2" operator1="abc" operator2="yyy" operator3="zzz"></record>
    <record id="5" operator1="xxx" operator2="lkj" operator3="tyu"></record>
    <record id="10" operator1="rrr" operator2="yyy" operator3="zzz"></record>
</data>

修改:也没有@local-key

的命名模板
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl">
    <xsl:key name="kItemByLocal" match="record[@local-key]" use="@local-key"/>
    <xsl:param name="pAttNames" select="'operator1 operator2 operator3'"/>
    <xsl:template match="/">
        <xsl:variable name="vFirstRTF">
            <xsl:apply-templates/>
        </xsl:variable>
        <xsl:apply-templates select="msxsl:node-set($vFirstRTF)/node()"/>
    </xsl:template>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="record[not(@local-key)]">
        <xsl:variable name="vAttNames"
                      select="concat(' ',$pAttNames,' ')"/>
        <xsl:copy>
            <xsl:attribute name="local-key">
                <xsl:for-each select="@*[contains(
                                             $vAttNames,
                                             concat(' ',name(),' ')
                                                 )]">
                    <xsl:sort select="substring-before(
                                             $vAttNames,
                                             concat(' ',name(),' ')
                                                      )"/>
                    <xsl:value-of select="concat(name(),'++',.,'++')"/>
                </xsl:for-each>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="record[@local-key]
                               [count(.|key('kItemByLocal',@local-key)[1])
                                 != 1]|@local-key"/>
</xsl:stylesheet>

注意:如果您确定所有元素的属性顺序相同,那么您可以删除排序。

答案 1 :(得分:2)

使用此转换(简单而无需生成新的样式表):

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

 <xsl:param name="pAttribs">
 <name>operator1</name>
 <name>operator2</name>
 <name>operator3</name>
 </xsl:param>

 <xsl:variable name="vAttribs" select=
    "document('')/*/xsl:param[@name='pAttribs']"/>

 <xsl:key name="kRecByAtts" match="record"
   use="@___g_key"/>

 <xsl:template match="node()|@*">
               <xsl:copy>
                       <xsl:apply-templates select="node()|@*"/>
               </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
 <xsl:variable name="vrtdPass1">
   <xsl:apply-templates/>
 </xsl:variable>

 <xsl:variable name="vPass1" select=
  "ext:node-set($vrtdPass1)/*"/>

 <xsl:apply-templates select="$vPass1"/>
 </xsl:template>

 <xsl:template match="record[not(@___g_key)]">
 <xsl:copy>
   <xsl:copy-of select="@*"/>

   <xsl:attribute name="___g_key">
    <xsl:for-each select="@*[name()=$vAttribs/name]">
      <xsl:sort select="name()"/>

       <xsl:value-of select=
          "concat('___Attrib___',name(),'___Value___',.,'+++')"/>
    </xsl:for-each>
   </xsl:attribute>
 </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "record[@___g_key]
         [not(generate-id()
             =
               generate-id(key('kRecByAtts', @___g_key)[1])
              )
          ]
   "/>

  <xsl:template match="@___g_key"/>
</xsl:stylesheet>

应用于上一个问题的XML文档时

<data id = "root">
    <record id="1" operator1='xxx' operator2='yyy' operator3='zzz'/>
    <record id="2" operator1='abc' operator2='yyy' operator3='zzz'/>
    <record id="3" operator1='abc' operator2='yyy' operator3='zzz'/>
    <record id="4" operator1='xxx' operator2='yyy' operator3='zzz'/>
    <record id="5" operator1='xxx' operator2='lkj' operator3='tyu'/>
    <record id="6" operator1='xxx' operator2='yyy' operator3='zzz'/>
    <record id="7" operator1='abc' operator2='yyy' operator3='zzz'/>
    <record id="8" operator1='abc' operator2='yyy' operator3='zzz'/>
    <record id="9" operator1='xxx' operator2='yyy' operator3='zzz'/>
    <record id="10" operator1='rrr' operator2='yyy' operator3='zzz'/>
</data>

产生了想要的正确结果

<data id="root">
   <record id="1" operator1="xxx" operator2="yyy" operator3="zzz"/>
   <record id="2" operator1="abc" operator2="yyy" operator3="zzz"/>
   <record id="5" operator1="xxx" operator2="lkj" operator3="tyu"/>
   <record id="10" operator1="rrr" operator2="yyy" operator3="zzz"/>
</data>

答案 2 :(得分:1)

如果要将属性名称作为参数传递,则一种方法可以是两步转换,其中第一步采用任何XML输入,只需将属性名称和元素名称作为参数生成第二个样式表,然后消除了重复。 以下是第一个样式表示例:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common"
  xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
  exclude-result-prefixes="axsl exsl"  
  version="1.0">

  <xsl:param name="parent-name" select="'items'"/>
  <xsl:param name="element-name" select="'item'"/>
  <xsl:param name="att-names" select="'att1,att2'"/>
  <xsl:param name="sep" select="'|'"/>

  <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>

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

  <xsl:variable name="key-value">
    <xsl:text>concat(</xsl:text>
    <xsl:call-template name="define-values">
      <xsl:with-param name="att-names" select="$att-names"/>
    </xsl:call-template>
    <xsl:text>)</xsl:text>
  </xsl:variable>

  <xsl:template name="define-values">
    <xsl:param name="att-names"/>
    <xsl:choose>
      <xsl:when test="contains($att-names, ',')">
        <xsl:value-of select="concat('@', substring-before($att-names, ','), ',&quot;', $sep, '&quot;,')"/>
        <xsl:call-template name="define-values">
          <xsl:with-param name="att-names" select="substring-after($att-names, ',')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="concat('@', $att-names)"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="/">
    <axsl:stylesheet version="1.0">
      <axsl:output indent="yes"/>
      <axsl:key name="k1" match="{$parent-name}/{$element-name}" use="{$key-value}"/>
      <axsl:template match="@* | node()">
        <axsl:copy>
          <axsl:apply-templates select="@* | node()"/>
        </axsl:copy>
      </axsl:template>
      <axsl:template match="{$parent-name}">
        <axsl:copy>
          <axsl:apply-templates select="@*"/>
          <axsl:apply-templates select="{$element-name}[generate-id() = generate-id(key('k1', {$key-value})[1])]"/>
        </axsl:copy>
      </axsl:template>
    </axsl:stylesheet>
  </xsl:template>

</xsl:stylesheet>

需要四个参数:

  1. parent-name:包含要​​消除重复项的元素的元素的名称
  2. element-name:您要消除重复项的元素的名称
  3. att-names:以逗号分隔的属性名称列表
  4. sep:输入XML
  5. 中属性值不应出现的分隔符

    然后,样式表生成第二个样式表,该样式表应用Muenchian分组以消除重复。例如,使用样式表中给出的默认参数,Saxon 6.5.5会生成以下样式表:

    <axsl:stylesheet xmlns:axsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
       <axsl:output indent="yes"/>
       <axsl:key name="k1" match="items/item" use="concat(@att1,&#34;|&#34;,@att2)"/>
       <axsl:template match="@* | node()">
          <axsl:copy>
             <axsl:apply-templates select="@* | node()"/>
          </axsl:copy>
       </axsl:template>
       <axsl:template match="items">
          <axsl:copy>
             <axsl:apply-templates select="@*"/>
             <axsl:apply-templates select="item[generate-id() = generate-id(key('k1', concat(@att1,&#34;|&#34;,@att2))[1])]"/>
          </axsl:copy>
       </axsl:template>
    </axsl:stylesheet>
    

    这可以应用于像

    这样的XML文档
    <items>
      <item att1="a" att2="1" att3="A"/>
      <item att1="b" att2="1" att3="A"/>
      <item att1="a" att2="1" att3="B"/>
      <item att1="c" att2="2" att3="A"/>
      <item att1="d" att2="3" att3="C"/>
    </items>
    

    ,输出

    <items>
       <item att1="a" att2="1" att3="A"/>
       <item att1="b" att2="1" att3="A"/>
       <item att1="c" att2="2" att3="A"/>
       <item att1="d" att2="3" att3="C"/>
    </items>