1

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

是否有另一种方法来实现重复节点删除?如果它不如 Munechian 方法那么有效,那也没关系。

从上一个问题更新:

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>
4

3 回答 3

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>

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

于 2011-01-06T14:05:35.147 回答
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>
于 2011-01-06T14:58:02.883 回答
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 的属性值中的分隔符

然后样式表生成第二个样式表,该样式表应用 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>
于 2011-01-06T12:31:38.613 回答