xslt对排序数据应用模板

时间:2017-08-01 09:49:25

标签: xml sorting xslt filter

我是xslt转换的新手,我遇到了一些麻烦。 我需要对元素进行排序和过滤,在下面的示例中,我设法使用两个xlt转换对输入xml进行排序和过滤。 我的问题是:如何使用单个xsl文件对排序数据进行排序和过滤? 提前谢谢。

XML输入:



<?xml version="1.0" encoding="UTF-8"?>
<root>
    <data>
        <id>00000_1111_2222</id>
        <startedAt>2017-08-21T11:55:08.382Z</startedAt>
        <endedAt>2017-08-21T12:07:08.539Z</endedAt>
        <positions>
            <timestamp>2017-08-21T11:55:28.041Z</timestamp>
            <latitude>40.2407009</latitude>
            <longitude>10.7750499</longitude>     
        </positions>
        <positions>
            <timestamp>2017-08-21T11:55:28.041Z</timestamp>
            <latitude>40.2409364</latitude>
            <longitude>10.7748426</longitude>
        </positions>
        <positions>
            <timestamp>2017-08-21T11:55:38.041Z</timestamp>
            <latitude>40.240409</latitude>
            <longitude>10.7751432</longitude>    
        </positions>
    </data>
</root>
&#13;
&#13;
&#13;

XSL排序:

&#13;
&#13;
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs" version="2.0">
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="data">
        <xsl:copy>
            <xsl:apply-templates select="*[not(self::positions)]"/>
            <xsl:apply-templates select="positions">
                <xsl:sort select="timestamp" order="descending"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>
&#13;
&#13;
&#13;

XSL过滤:

&#13;
&#13;
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="data">
        <xsl:copy>
            <xsl:apply-templates select="*[not(self::positions)]" />
            <xsl:copy-of select="positions[position() &lt;= 4]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
&#13;
&#13;
&#13;

如果我保留原始xmlstructure,它是否与此xsl相关?

&#13;
&#13;
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:output omit-xml-declaration="no" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <!-- Identity template -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Sort and filter positions elements -->
    <xsl:template match="data">
        <xsl:copy>
            <xsl:for-each select="positions">
                <xsl:sort select="timestamp" order="descending"/>
                <xsl:if test="position() &lt;= 2">
                    <xsl:apply-templates select="."/>
                </xsl:if>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
&#13;
&#13;
&#13;

我的预期输出XML将是:

&#13;
&#13;
<?xml version="1.0" encoding="UTF-8"?>
<root>
   <data>
        <id>00000_1111_2222</id>
        <startedAt>2017-08-21T11:55:08.382Z</startedAt>
        <endedAt>2017-08-21T12:07:08.539Z</endedAt>
        
      <positions>
         <timestamp>2017-08-21T11:55:38.041Z</timestamp>
         <latitude>40.240409</latitude>
         <longitude>10.7751432</longitude>
      </positions>
      <positions>
         <timestamp>2017-08-21T11:55:28.041Z</timestamp>
         <latitude>40.2407009</latitude>
         <longitude>10.7750499</longitude>
      </positions>
   </data>
</root>
&#13;
&#13;
&#13;

但是通过这种方式,我放弃了所有元素,而不是&#34;位置&#34;元素:id,startedAt,endsAt。 所以我试着添加:<xsl:apply-templates select="* [not(self :: positions)]&#34; /&gt;在<xsl:template match="data">之后它可以工作,但是如果所有不是&#34;位置&#34;孩子们,是位置元素之后?这样我将松开原始的xml结构/顺序。有没有办法以通用的方式实现这最后的事情? 谢谢。

1 个答案:

答案 0 :(得分:1)

鉴于XSLT 2.0,您可以使用

<xsl:variable name="sorted-positions" as="element(positions)*">
  <xsl:perform-sort select="positions">
    <xsl:sort select="timestamp" order="descending"/>
  </xsl:perform-sort>
</xsl:variable>

然后<xsl:copy-of select="$sorted-positions[position() le 4]"/>(或者当然是这些元素的应用模板)。

或者您可以使用例如

        <xsl:apply-templates select="positions">
            <xsl:sort select="timestamp" order="descending"/>
        </xsl:apply-templates>

然后

<xsl:template match="positions">
  <xsl:if test="position() le 4">
    <xsl:next-match/>
  </xsl:if>
</xsl:template>

使用XSLT 1.0,您当然可以使用例如。

<xsl:for-each select="positions">
  <xsl:sort select="timestamp" order="descending"/>
  <xsl:if test="position() &lt;= 4">
    <xsl:apply-templates select="."/>
  </xsl:if>
</xsl:for-each>

当然也可以选择使用copy-of代替apply-templates

最后,使用XSLT 3.0并支持sort函数(https://www.w3.org/TR/xpath-functions-31/#func-sort),您可以直接apply-templates查看已排序的序列(或者在您的情况下,reverse d排序序列以降序排序)和过滤后的序列:

<xsl:template match="data">
    <xsl:copy>
        <xsl:apply-templates select="* except positions, reverse(sort(positions, (), function($p) { $p/timestamp}))[position() le 4]"/>
    </xsl:copy>
</xsl:template>