XSLT基于子孙的子集计算“相同”元素

时间:2010-02-17 00:05:19

标签: html xml xslt

我正在将XML转换为HTML。在XML的一部分中,我需要计算在比较特定后代时看起来“相同”的元素,而其他后代和任何属性必须被忽略。

这是我的XML的简化示例。真实的东西要复杂得多;我刚刚删除了与计数无关的元素:

<complaints>
    <lodgedComplaint>
        <defendant>First Person</defendant>
        <charge>
            <actionNumber>1</actionNumber>
            <offence>
                <offenceSection>
                    <legislation>SA1</legislation>
                    <description>Stealing</description>
                </offenceSection>
                <placeOfOffence>Sydney</placeOfOffence>
                <dateOfOffence>2010-01-01</dateOfOffence>
                <particular>value=20</particular>
            </offence>
        </charge>
        <charge>
            <actionNumber>2</actionNumber>
            <offence>
                <offenceSection>
                    <legislation>SA2</legislation>
                    <description>Theft</description>
                </offenceSection>
                <placeOfOffence>Sydney</placeOfOffence>
                <dateOfOffence>2010-01-01</dateOfOffence>
            </offence>
        </charge>
        <charge>
            <actionNumber>3</actionNumber>
            <offence>
                <offenceSection>
                    <legislation>SA2</legislation>
                    <description>Theft</description>
                </offenceSection>
                <placeOfOffence>London</placeOfOffence>
                <dateOfOffence>2010-01-01</dateOfOffence>
            </offence>
        </charge>
        <charge>
            <actionNumber>4</actionNumber>
            <offence>
                <offenceSection>
                    <legislation>SA1</legislation>
                    <description>Stealing</description>
                </offenceSection>
                <placeOfOffence>Sydney</placeOfOffence>
                <dateOfOffence>2010-01-01</dateOfOffence>
                <particular>value=50</particular>
            </offence>
        </charge>
        <charge>
            <actionNumber>5</actionNumber>
            <offence>
                <offenceSection>
                    <legislation>SA2</legislation>
                    <description>Theft</description>
                </offenceSection>
                <placeOfOffence>Sydney</placeOfOffence>
                <dateOfOffence>2010-01-02</dateOfOffence>
            </offence>
        </charge>
    </lodgedComplaint>
    <lodgedComplaint>
        <defendant>Second Person</defendant>
        <charge>
            <actionNumber>1</actionNumber>
            <offence>
                <offenceSection>
                    <legislation>SA1</legislation>
                    <description>Stealing</description>
                </offenceSection>
                <placeOfOffence>Sydney</placeOfOffence>
                <dateOfOffence>2010-01-01</dateOfOffence>
                <particular>value=35</particular>
            </offence>
        </charge>
    </lodgedComplaint>
</complaints>

在每个lodgedComplaint中,如果其立法,描述,placeOfOffence和dateOfOffence元素匹配,则任何两项费用应视为相同。必须忽略actionNumber和特定元素(特定元素是可选的,并且在我给出的模式中无界限。)

所需的输出应如下所示:

First Person
    2 counts of Stealing at Sydney on 1/1/2010 under SA1
    1 count of Theft at Sydney on 1/1/2010 under SA2
    1 count of Theft at London on 1/1/2010 under SA2
    1 count of Theft at Sydney on 2/1/2010 under SA2
Second Person
    1 count of Stealing at Sydney on 1/1/2010 under SA1

这是我尝试过的XSLT,基于我在Stack Overflow和其他地方阅读的内容。它不起作用;收费细节根本没有出现。我认为我使用concat()会导致问题。 我怎么能这样做?

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="UTF-8" indent="yes"/>
    <xsl:key name="matchOffence" match="offence" use="concat(offenceSection/legislation,offenceSection/description,placeOfOffence,dateOfOffence)"/>
    <xsl:template match="/">
        <html>
            <head>
                <title>CRIMES Listings</title>
                <link rel="stylesheet" type="text/css" href="global_styles.css"/>
                <link rel="stylesheet" type="text/css" href="styles.css"/>
            </head>
            <body>
                <xsl:apply-templates select="complaints/lodgedComplaint"/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="lodgedComplaint">
        <br/>
        <xsl:value-of select="defendant"/>
        <xsl:for-each select="charge/offence[generate-id(concat(offenceSection/legislation,offenceSection/description,placeOfOffence,dateOfOffence))=generate-id(key('matchOffence',.))]">
            <br/>
            <xsl:value-of select="count(../../charge/offence[(offenceSection/legislation=current()/offenceSection/legislation) and (offenceSection/description=current()/offenceSection/description) and (placeOfOffence=current()/placeOfOffence) and (dateOfOffence=current()/dateOfOffence)])"/>
            counts of <b><xsl:value-of select="offenceSection/description"/></b>
            at <b><xsl:value-of select="placeOfOffence"/></b>
            on <b><xsl:call-template name="date">
                <xsl:with-param name="text" select="dateOfOffence"/>
            </xsl:call-template></b>
            under <b><xsl:value-of select="offenceSection/legislation"/></b>
        </xsl:for-each>
    </xsl:template>

    <xsl:template name="date">
        <xsl:param name="text" select="."/>
        <xsl:choose>
            <xsl:when test="contains($text,'-')">
                <xsl:call-template name="date">
                    <xsl:with-param name="text" select="substring-after($text,'-')"/>
                </xsl:call-template>/<xsl:value-of select="substring-before($text,'-')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$text"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:1)

样式表有两个问题。

  1. 您正在尝试generate-id()使用concat()的结果,这会产生字符串结果。 generate-id()仅适用于节点。

    应该重写为:<xsl:for-each select="charge/offence[generate-id(.)=generate-id(key('matchOffence',concat(offenceSection/legislation,./offenceSection/description,placeOfOffence,dateOfOffence)))]">

  2. 另一个问题是用于key的字符串值不是唯一的。第二个人的offence与第一个人的offense之一具有相同的值。这可以防止您为第二个人生成输出。您可以通过在密钥中添加key元素的值来使defendant更加独特。

    调整key后,您显然需要调整for-each

  3. 将所有内容放在样式表中:

    <?xml version="1.0" encoding="UTF-8"?>    
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="html" encoding="UTF-8" indent="yes"/>
        <xsl:key name="matchOffence" match="offence" use="concat(../../defendant,offenceSection/legislation,offenceSection/description,placeOfOffence,dateOfOffence)"/>
        <xsl:template match="/">
            <html>
                <head>
                    <title>CRIMES Listings</title>
                    <link rel="stylesheet" type="text/css" href="global_styles.css"/>
                    <link rel="stylesheet" type="text/css" href="styles.css"/>
                </head>
                <body>
                    <xsl:apply-templates select="complaints/lodgedComplaint"/>
                </body>
            </html>
        </xsl:template>
    
        <xsl:template match="lodgedComplaint">
            <br/>
            <xsl:value-of select="defendant"/>
            <xsl:for-each select="charge/offence[generate-id(.)=generate-id(key('matchOffence',concat(../../defendant,offenceSection/legislation,./offenceSection/description,placeOfOffence,dateOfOffence)))]">
    
              <br/>
                <xsl:value-of select="count(../../charge/offence[(offenceSection/legislation=current()/offenceSection/legislation) and (offenceSection/description=current()/offenceSection/description) and (placeOfOffence=current()/placeOfOffence) and (dateOfOffence=current()/dateOfOffence)])"/>
                counts of <b><xsl:value-of select="offenceSection/description"/></b>
                at <b><xsl:value-of select="placeOfOffence"/></b>
                on <b><xsl:call-template name="date">
                    <xsl:with-param name="text" select="dateOfOffence"/>
                </xsl:call-template></b>
                under <b><xsl:value-of select="offenceSection/legislation"/></b>
    
            </xsl:for-each>
        </xsl:template>
    
        <xsl:template name="date">
            <xsl:param name="text" select="."/>
            <xsl:choose>
                <xsl:when test="contains($text,'-')">
                    <xsl:call-template name="date">
                        <xsl:with-param name="text" select="substring-after($text,'-')"/>
                    </xsl:call-template>/<xsl:value-of select="substring-before($text,'-')"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$text"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    </xsl:stylesheet>