使用 xslt 对列表进行排序并消除 XML 文件中的重复项 NEW

问题描述 投票:0回答:1

我已经问过这个问题,并且我采取了错误的示例列表,这导致了没有用的答案。然后我以为我已经找到了解决方案,但它导致了错误的结果。那我再问一下吧。

Start XML 是一个列表,其中包含许多出现的 different 元素,这些元素具有包含 different 值的 different 属性。 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>

结果应该是一个列表,其中包含每个元素+属性+值组合仅出现一次并且应按以下顺序按字母顺序排序:

  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>

非常欢迎任何帮助,对于第一次尝试中的误导性问题深表歉意!

xml sorting xslt
1个回答
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])"

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

© www.soinside.com 2019 - 2024. All rights reserved.