帮助XSLT转换XML:分组和选择具有最大值的元素

时间:2011-04-28 05:19:12

标签: xml xslt

我有当前的XML:

<DocumentElement>
  <Customer>
    <CustomerId>2315</CustomerId>
    <Date>2011-04-28 14:14:00</Date>   
    <VersionNumber>1</VersionNumber>
    <GUID>2E05DE20-02A0-425D-944D-65E5E744FF8A</GUID>
  </Customer>
  <Customer>
    <CustomerId>2316</CustomerId>
    <Date>2011-04-28 15:03:00</Date>
    <VersionNumber>2</VersionNumber>
    <GUID>2E05DE20-02A0-425D-944D-65E5E744FF8A</GUID>
  </Customer>
  <Customer>
    <CustomerId>2317</CustomerId>
    <Date>2011-04-28 15:03:00</Date>
    <VersionNumber>1</VersionNumber>
    <GUID>9995DE20-02A0-425D-944D-65E5E744FF8A</GUID>
  </Customer>
</DocumentElement>

我要做的是过滤掉每个GUID中具有最高版本号的一个元素,即将上面的文档转换为:

<DocumentElement>
  <Customer>
    <CustomerId>2316</CustomerId>
    <Date>2011-04-28 15:03:00</Date>
    <VersionNumber>2</VersionNumber>
    <GUID>2E05DE20-02A0-425D-944D-65E5E744FF8A</GUID>
  </Customer>
  <Customer>
    <CustomerId>2317</CustomerId>
    <Date>2011-04-28 15:03:00</Date>
    <VersionNumber>1</VersionNumber>
    <GUID>9995DE20-02A0-425D-944D-65E5E744FF8A</GUID>
  </Customer>
</DocumentElement>

任何能指出正确方向的人都会从哪里开始?

提前致谢。

2 个答案:

答案 0 :(得分:2)

作为练习,我尝试使用XSLT 2.0和XPath 2.0来解决这个问题:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes" omit-xml-declaration="no" />

  <xsl:template match="/DocumentElement">
   <xsl:copy>
    <xsl:for-each-group select="Customer" group-by="GUID">
     <xsl:copy-of select="current-group()
                          [VersionNumber=max(current-group()/VersionNumber)]" />
    </xsl:for-each-group>
   </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

主要差异:

  • 使用XSLT 2.0,您无需像this other answer中那样定义xsl:key,但可以使用xsl:for-each-group ... group-by="GUID"

  • 使用XPath 2.0,您拥有fn:max(...)功能

答案 1 :(得分:1)

我首先在GUID上声明key,这样可以轻松处理不同的GUID;然后,根据GUID,在其VersionNumber上对相应的Customer元素进行排序,然后只复制第一个(xslt-1.0):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:key name="byGUID" match="Customer" use="GUID"/>
    <xsl:template match="/*">
        <xsl:copy>
            <!-- process first Customer for each distinct GUID -->
            <xsl:apply-templates select="//Customer[generate-id()=generate-id(key('byGUID',GUID))]"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*">
        <!-- sort and process all Customers with the same GUID -->
        <xsl:apply-templates select="key('byGUID',GUID)" mode="copyFirst">
            <xsl:sort select="VersionNumber" order="descending"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*" mode="copyFirst">
        <!-- only copy the first (highest VersionNumber) -->
        <xsl:if test="position()=1">
            <xsl:copy-of select="."/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet> 

使用此节点集交集是处理每个不同GUID的第一个Customer的另一种方法:

<xsl:apply-templates select="//Customer[count(.|key('byGUID',GUID)[1])=1]"/>