代码之家  ›  专栏  ›  技术社区  ›  AJ.

使用XSLT进行透视

  •  5
  • AJ.  · 技术社区  · 17 年前

    我有这样一个xml文件:

    <root>
        <item>
            <name>one</name>
            <status>good</status>
        </item>
        <item>
            <name>two</name>
            <status>good</status>
        </item>
        <item>
            <name>three</name>
            <status>bad</status>
        </item>
        <item>
            <name>four</name>
            <status>ugly</status>
        </item>
        <item>
            <name>five</name>
            <status>bad</status>
        </item>
    </root>
    

    我想使用XSLT对此进行转换,以获得如下结果:

    <root>
        <items><status>good</status>
            <name>one</name>
            <name>two</name>
        </items>
        <items><status>bad</status>
            <name>three</name>
            <name>five</name>
        </items>
        <items><status>ugly</status>
            <name>four</name>
        </items>
    </root>
    

    换句话说,我得到一个项目列表,每个项目都有一个状态,我想把它转换成一个状态列表,每个状态列表都有一个项目列表。

    我最初的想法是依次应用匹配每个状态类型的模板,但这意味着我必须知道状态的完整列表。有更好的方法吗?

    谢谢你的帮助。

    4 回复  |  直到 17 年前
        1
  •  8
  •   annakata    17 年前

    快去营救我!

    <?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" indent="yes" encoding="UTF-8"/>
    
        <xsl:key name="muench" match="/root/item/status" use="."/>
    
        <xsl:template match="/">
            <root>
            <xsl:for-each select="/root/item/status[generate-id() = generate-id(key('muench',.)[1])]">
                <xsl:call-template name="pivot">
                    <xsl:with-param name="status" select="."/>
                </xsl:call-template>
            </xsl:for-each>
            </root>
        </xsl:template>
    
        <xsl:template name="pivot">
            <xsl:param name="status"/>
            <items>
                <status><xsl:value-of select="$status"/></status>
                <xsl:for-each select="/root/item[status=$status]">
                    <name><xsl:value-of select="name"/></name>
                </xsl:for-each>
            </items>
        </xsl:template>
    
    </xsl:stylesheet>
    
        2
  •  4
  •   Dimitre Novatchev    17 年前

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     >
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
        <!--                                   -->
        <xsl:key name="kStatByVal" 
             match="status" use="."/>
        <!--                                   -->
        <xsl:key name="kItemByStat" 
             match="item" use="status"/>
        <!--                                   -->
        <xsl:variable name="vDoc" select="/"/>
    
        <xsl:template match="/">
          <top>
            <xsl:for-each select=
            "/*/*/status[generate-id()
                        =
                         generate-id(key('kStatByVal',.)[1])
                        ]">
              <items>
                <status><xsl:value-of select="."/></status>
              <xsl:for-each select="key('kItemByStat', .)">
                <xsl:copy-of select="name"/>
              </xsl:for-each>
              </items>
            </xsl:for-each>
          </top>
        </xsl:template>
    </xsl:stylesheet>
    

    将此转换应用于原始XML文档时 :

    <root>
        <item>
            <name>one</name>
            <status>good</status>
        </item>
        <item>
            <name>two</name>
            <status>good</status>
        </item>
        <item>
            <name>three</name>
            <status>bad</status>
        </item>
        <item>
            <name>four</name>
            <status>ugly</status>
        </item>
        <item>
            <name>five</name>
            <status>bad</status>
        </item>
    </root>
    

    产生了想要的结果

    <top>
        <items>
            <status>good</status>
            <name>one</name>
            <name>two</name>
        </items>
        <items>
            <status>bad</status>
            <name>three</name>
            <name>five</name>
        </items>
        <items>
            <status>ugly</status>
            <name>four</name>
        </items>
    </top>
    

    :

    1. 这个 Muenchian method for grouping

    2. 使用 <xsl:key> key()

        3
  •  0
  •   gizmo    17 年前

    这取决于您的xslt引擎。如果您使用的XSLT1.0没有任何扩展,那么您的方法肯定是最好的。

    另一方面,如果允许使用exslt(特别是节点集扩展)或xslt 2.0,则可以采用更通用的方式:

    1. 收集所有可用状态
    2. 在该节点集上迭代时,通过基于迭代中的当前元素筛选您的状态来创建轴心。

    但是在这样做之前,考虑如果你只有几组状态并且添加另一个状态是非常罕见的,那么它可能是多余的。

        4
  •  0
  •   Kai Hartmann    12 年前

    在XSLT2.0中,您可以用其标准分组机制来替换慕尼黑分组。应用到给定答案时,xslt将如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
    
        <xsl:key name="muench" match="/root/item/status" use="."/>
    
        <xsl:template match="/">
            <root>
            <xsl:for-each-group select="/root/item/status" group-by="key('muench', .)">
                <xsl:call-template name="pivot">
                    <xsl:with-param name="status" select="."/>
                </xsl:call-template>
            </xsl:for-each-group>
            </root>
        </xsl:template>
    
        <xsl:template name="pivot">
            <xsl:param name="status"/>
            <items>
                <status><xsl:value-of select="$status"/></status>
                <xsl:for-each select="/root/item[status=$status]">
                    <name><xsl:value-of select="name"/></name>
                </xsl:for-each>
            </items>
        </xsl:template>
    
    </xsl:stylesheet>