在确定每个唯一记录的唯一最高得分后对转换的XML进行排序

时间:2014-03-06 17:21:23

标签: xml sorting xslt

我编译了以下转换脚本。其目的是为每个唯一的行ID找到最高分。遗憾的是,这是通过将解决方案拼凑到类似问题而创建的,我并不完全理解该解决方案。我还试图将其纳入转换中,即按降序对返回的记录进行排序。

示例代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" standalone="yes"/>
   <xsl:key name="kRecordByRowID" match="record" use="CON_ROW_ID"/>
   <xsl:template match="record" />
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
        <xsl:apply-templates select="node()|@*"> <!-- Insert sort here for partially working sorting--> 
            <xsl:sort select="/SCORE" order="descending"/>        
        </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="record[count(.|key('kRecordByRowID',CON_ROW_ID)[1]) = 1]">
        <xsl:for-each select="key('kRecordByRowID',CON_ROW_ID)"> 
            <xsl:if test="position()=1">
                <xsl:call-template name="identity"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

示例输入:

<records>   
    <record>
        <CON_ROW_ID>F</CON_ROW_ID>
        <SCORE>80</SCORE>
    </record>
    <record>
        <CON_ROW_ID>D</CON_ROW_ID>
        <SCORE>90</SCORE>
    </record>
    <record>
        <CON_ROW_ID>D</CON_ROW_ID>
        <SCORE>75</SCORE>
    </record>
    <record>
        <CON_ROW_ID>F</CON_ROW_ID>
        <SCORE>85</SCORE>
    </record>
</records>

正如您在代码中看到的那样,有一条注释说我可以将排序代码()插入到标记中,但它会返回不正确的结果。结果被分类到一个点,然后它们变得随机。

此分类代码应放在何处以正确返回分数降序列表?

2 个答案:

答案 0 :(得分:0)

没有/SCORE元素(sort永远不会选择它)。您想要的是使用SCORE中的上下文record元素,这样就可以SCORE

<xsl:template match="node()|@*" name="identity">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"> <!-- Insert sort here for partially working sorting--> 
            <xsl:sort select="SCORE" order="descending"/>        
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

答案 1 :(得分:0)

  

可悲的是,这是通过拼凑相似的解决方案来创造的   问题,我不完全理解解决方案。

为什么不以更明智的方式重写它,例如:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="record-by-ID" match="record" use="CON_ROW_ID"/>

<xsl:template match="/">
    <output>
        <!-- for each first record in its group -->
        <xsl:for-each select="records/record[count(.|key('record-by-ID',CON_ROW_ID)[1]) = 1]">
            <!-- sort the group by score -->
            <xsl:for-each select="key('record-by-ID',CON_ROW_ID)">
                <xsl:sort select="SCORE" order="descending"/>   
                <!-- output the highest scoring record -->
                <xsl:if test="position() = 1">
                    <xsl:copy-of select="."/>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>

修改

  

每个唯一的CON_ROW_ID应该取最高分,然后   以SCORE降序再次返回这些。

如果你的处理器支持一些基本的EXSLT功能,那么这可能非常简短和甜蜜:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:set="http://exslt.org/sets"
xmlns:math="http://exslt.org/math"
extension-element-prefixes="set math">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="record-by-ID" match="record" use="CON_ROW_ID"/>

<xsl:template match="/">
    <output>
        <xsl:for-each select="set:distinct(records/record/CON_ROW_ID)">
            <xsl:sort select="math:max(key('record-by-ID', .)/SCORE)" order="descending"/>   
            <xsl:copy-of select="math:highest(key('record-by-ID', .)/SCORE)/parent::record"/>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>

否则你需要两次通过(在过程中使用EXSLT node-set()函数):

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="record-by-ID" match="record" use="CON_ROW_ID"/>

<xsl:template match="/">
    <!-- FIRST PASS -->
    <xsl:variable name="highest-scoring-records">
        <!-- for each first record in its group -->
        <xsl:for-each select="records/record[count(.|key('record-by-ID',CON_ROW_ID)[1]) = 1]">
            <!-- sort the group by score -->
            <xsl:for-each select="key('record-by-ID',CON_ROW_ID)">
                <xsl:sort select="SCORE" order="descending"/>   
                <!-- output the highest scoring record -->
                <xsl:if test="position() = 1">
                    <xsl:copy-of select="."/>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:variable>
    <!-- OUTPUT -->
    <output>
        <xsl:for-each select="exsl:node-set($highest-scoring-records)/record">
            <xsl:sort select="SCORE" order="descending"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
    </output>
</xsl:template>

</xsl:stylesheet>