使用xslt NEW

时间:2017-04-24 09:49:43

标签: xml sorting xslt

我已经问过这个问题了,我采用了一个错误的示例列表,这导致了无用的答案。然后我认为我找到了一个解决方案但它会导致错误的结果。所以让我再问一次。

启动XML是一个列表,其中包含许多不同元素的出现,这些元素具有不同的属性,其中包含不同的值。 示例XML:

<attributes>
        <para role="tocmain1"/>
        <para role="tocmain1"/>
        <other style="fix"/>
        <other style="fix1"/>
        <para role="tocmain2"/>
        <para role="tocmain2"/>
        <para role="tocmain2"/>
        <para role="tocmain3"/>
        <para role="tocmain3"/>
        <para language="de"/>
        <para language="de"/>
        <para role="tocmain3"/>
</attributes>

结果应该是一个列表,其中包含每个Element +属性+值组合只出现一次,并且应按此顺序按字母顺序排序:1。元素的字母顺序,2。字母顺序属性,3。按字母顺序排列的值。

示例结果:

<attributes>
     <other style="fix"/>
     <other style="fix1"/>
     <para language="de"/>
     <para role="tocmain1"/>
     <para role="tocmain2"/>
     <para role="tocmain3"/>    
</attributes>

现在我正在使用连续执行的两个xlst,问题是结果列表不完整:缺少元素+属性+值的某些组合。问题位于第一个模板中,因为我按属性值分组,而我只采用第一个出现。可以是相同的属性值与不同的属性一起使用。在这些情况下,缺少第二次出现。是否有可能根据属性+值的组合进行分组?

1。 XSLT(分组和删除重复项):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>       
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>      
  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>         
      <xsl:for-each-group select="*" group-by="@*">
        <xsl:sort select="@*"/> 
        <xsl:apply-templates select="current-group()[1]"/>          
      </xsl:for-each-group>                     
    </xsl:copy>
  </xsl:template>       
</xsl:stylesheet>

2。 XSLT(按字母顺序排序):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/">
        <attributes>
            <xsl:for-each select="attributes/node()">                 
                <xsl:sort select="name()" order="ascending"/> 
                <xsl:sort select="name(@*)" order="ascending"/>                   
                <xsl:sort select="@*" order="ascending"/>                  
                <xsl:copy-of select="."/>                  
            </xsl:for-each>   
        </attributes>
    </xsl:template>           
</xsl:stylesheet>

任何帮助都是非常受欢迎的,并且在第一次尝试时对于误导性的问题感到抱歉!

1 个答案:

答案 0 :(得分:1)

假设Saxon 9.7 PE或EE或AltovaXML 2017支持XSLT 3.0,您只需使用复合密钥:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:output indent="yes"/>  

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:for-each-group select="*" group-by="node-name(), node-name(@*[1]), @*[1]" composite="yes">
                <xsl:sort select="string(current-grouping-key()[1])"/>
                <xsl:sort select="string(current-grouping-key()[2])"/>
                <xsl:copy-of select="."/>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

使用XSLT 2.0,您可以使用嵌套的for-each-group s

<xsl:template match="/*">
    <xsl:copy>
        <xsl:for-each-group select="*" group-by="node-name(.)">
            <xsl:sort select="string(current-grouping-key())"/>
            <xsl:for-each-group select="current-group()" group-by="node-name(@*[1])">
                <xsl:sort select="string(current-grouping-key())"/>                 
                <xsl:for-each-group select="current-group()" group-by="@*[1]">
                    <xsl:copy-of select="."/>
                </xsl:for-each-group>
            </xsl:for-each-group>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

或使用使用字符串连接创建的复合密钥。 group-by="concat(node-name(), '|', node-name(@*[1]), '|', @*[1])"

这些建议假定元素可以具有不同的属性,但每个元素只有一个属性。