2010-10-12 106 views
0

我使用XSLT1.0(处理器无法处理2。0),并且具有试图基的XML结构的输出的问题:XSL按元素分组xml

<行>
<顺序>
<文本>一些顺序文本1
< /文本>
< /顺序>
</row>

<row>
<付款>
<文本>某些付款文本1
< /文本>
< /付款>
< /行>

<行>
<顺序>
<文本>一些顺序文本2
< /文本>
< /顺序>
< /行>

<行>
<接触>
<文本>一些联系方式1
</text >
< /接触>
< /行>

<行>
<接触>
<文本>一些联系方式2
< /文本>
< /接触>
< /行>

今天我们选择所有的行,并呼吁每个应用模板(每种类型都有自己的模板,写出它的身体),创建等的输出:

订单:有的为了text1的
订单:一些秩序文本2
付款方式:一些付款的text1
联系方式:一些接触点评详情
联系方式:一些接触details2

但我想是(在XSLT 1。0)到组输出,使:

订购

  1. 某种秩序的text1
  2. 一些命令文本2

付款

  1. 一些支付的text1

联系

  1. 一些接触点评详情
  2. 一些接触details2

显然还有很多其他的元素类型比秩序,支付和这里所涉及的外在因素的名称,以便选择接触不是一个解决方案。

编辑

泰,一些伟大的答案,如果我有一个结构会如何Muenchian分组的解变化说

<customers> 
    <person> 
    <row>....</row> (row is same as above) 
    <row>....</row> 
    </person> 
    <person> 
    <row>....</row> 
    <row>....</row> 
    <row>....</row> 
    </person> 

那么关键:

<xsl:key name="type" match="row/*" use="local-name()"/> 

会选择所有横跨所有人的行,这不是我想要的。感谢您的好评。

+0

可能重复的[如何通过在XSLT元素应用组( http://stackoverflow.com/questions/2146648/how-to-apply-group-by-on-xslt-elements) – annakata 2010-10-12 11:35:49

+0

很多,很多,很多*,倍。 – annakata 2010-10-12 11:36:08

+0

@ user408346:请提出一个新问题。不要向这个问题添加新的问题。 – 2010-10-13 12:40:44

回答

1

尝试:

<xsl:template match="(parent element-whatever contains the 'row' elements)"> 
    <xsl:apply-templates> 
    <xsl:sort select="name(*)" /> 
    </xsl:apply-templates> 
</xsl:template> 

此排序由第一个孩子的名字行元素。

这个模板增加了一个头:

<xsl:template match="row"> 
    <xsl:copy> 
    <xsl:if test="not(preceding-sibling::*[name(*) = name(current()/*)])"> 
     <!-- Output header here --> 
     <xsl:value-of select="name(*)" /> 
    </xsl:if> 
    <xsl:apply-templates select="@* | node()"/> 
    </xsl:copy> 
</xsl:template> 

测试基本上说“输出结果,如果没有以前的兄弟姐妹相同的名称”。

+0

除此之外不会打印出节标题(“订单”)。 – LarsH 2010-10-12 10:43:24

+0

P.S.但我喜欢使用排序作为分组事物的简单方法。 +1 – LarsH 2010-10-12 11:39:11

+0

真的,我想;我将输出解释为描述而不是确切的文字。我已经通过添加标题的方式进行了编辑。编辑:嘿,只是注意到,这或多或少是你的答案:)我只是没有进入格式化输出的细节。 – Flynn1179 2010-10-12 13:00:01

4

在XSLT 1.0中执行此操作需要使用Muenchian grouping,但在XSLT 2.0中更容易(在我看来)使用xsl:for-each-group来解决。

下面的XSLT 1.0样式表会做你所要求的,关键是使用一个键(doh!),它将允许你在节点上分组本地名。

输入:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <row> 
    <order> 
     <text>some order text 1</text> 
    </order> 
    </row> 

    <row> 
    <payment> 
     <text>some payment text 1</text> 
    </payment> 
    </row> 

    <row> 
    <order> 
     <text>some order text 2</text> 
    </order> 
    </row> 

    <row> 
    <contact> 
     <text>some contact details 1</text> 
    </contact> 
    </row> 

    <row> 
    <contact> 
     <text>some contact details 2</text> 
    </contact> 
    </row> 
</root> 

XSLT:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="text"/> 
    <xsl:key name="type" match="row/*" use="local-name()"/> 

    <xsl:template match="root"> 
    <xsl:for-each select="row/*[ 
     generate-id() = generate-id(key('type', local-name())[1])]"> 
     <xsl:value-of select="local-name()"/> 
     <xsl:text>&#x0a;</xsl:text> 
     <xsl:for-each select="key('type', local-name())"> 
     <xsl:value-of select="concat(' ', position(), '. ')"/> 
     <xsl:apply-templates select="text"/> 
     <xsl:text>&#x0a;</xsl:text> 
     </xsl:for-each> 
    </xsl:for-each> 
    </xsl:template> 

</xsl:stylesheet> 

输出:

order 
    1. some order text 1 
    2. some order text 2 
payment 
    1. some payment text 1 
contact 
    1. some contact details 1 
    2. some contact details 2 
+0

+1为Muenchian喝彩。对于大数据集,这可能比我的回答更好。 – LarsH 2010-10-12 11:37:13

+0

+1好的答案,除了“brick”模板... – 2010-10-12 13:20:35

+0

Ty,一些很好的答案,如果我有一个说客户结构含有行的人(如上面),那么Muenchian分组解决方案如何改变,那么密钥定义如上定义遍历所有人的所有行?我希望它遍历每个人元素的所有行。 – user408346 2010-10-12 20:56:34

1

大厦@弗林的答案...

如果你有这样的模板父(你的样品中未画出):

<xsl:template match="row-parent"> 
    <xsl:apply-templates select="row"> 
    <xsl:sort select="name(*[1])" /> 
    </xsl:apply-templates> 
</xsl:template> 

注意,通过选择“行”,而不是默认的(所有儿童,包括文本节点),我们避免选择包含空格的文本节点,以及哪些不适合我们的输出。

然后才能添加章节标题,用于处理孩子的模板使用条件来看看这是其部分的第一行:

<xsl:template match="row"> 
    <xsl:variable name="childName" select="name(*[1])"/> 
    <!-- if this is the first row with an element child of this name --> 
    <xsl:if test="not(preceding-sibling::row[name(*[1]) = $childName])"> 
     <xsl:value-of select="concat('&#10;', 
     translate(substring($childName, 1, 1), $lower, $upper), 
     substring($childName, 2), '&#10;&#10;')"/> 
    </xsl:if> 

然后输出的每一行数据组,格式化你想要的:

<xsl:number level="any" count="row[name(*[1]) = $childName]" format=" 1. " 
     from="row-parent"/> 
    <xsl:value-of select="normalize-space(*[1])"/> 
    <xsl:text>&#10;</xsl:text> 
</xsl:template> 

像往常一样,$下限和上限$在模板(或CSS)的顶部,

<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/> 
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/> 
定义

,使样式表使用“文本”输出方法:

<xsl:output method="text"/> 

上述样式表上的输入的输出(A <row-parent>包装内)是:

Contact 

1. some contact details 1 
2. some contact details 2 

Order 

1. some order text 1 
2. some order text 2 

Payment 

1. some payment text 1 

备选地,更您可以使用Muenchian grouping:首先按子元素名称对行进行分组,然后(为每个组输出标题并处理该组中的所有行)。

1

除了与分组方法,这个样式表好的答案:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:variable name="vSort" select="'|order|payment|contact|'"/> 
    <xsl:template match="/"> 
     <xsl:apply-templates select="*/row"> 
      <xsl:sort select="string-length(
           substring-before($vSort, 
                concat('|', 
                 name(), 
                 '|')))"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="row/*"> 
     <xsl:variable name="vName" select="name()"/> 
     <xsl:variable name="vNumber"> 
      <xsl:number level="any" count="*[name()=$vName]" from="/"/> 
     </xsl:variable> 
     <xsl:if test="$vNumber = 1"> 
      <xsl:value-of select="concat(translate(substring(name(),1,1), 
                 'opc', 
                 'OPC'), 
              substring(name(),2), 
              '&#xA;')"/> 
     </xsl:if> 
     <xsl:value-of select="concat($vNumber,'. ',text,'&#xA;')"/> 
    </xsl:template> 
</xsl:stylesheet> 

输出(与井形成输入):

Order 
1. some order text 1 
Payment 
1. some payment text 1 
2. some order text 2 
Contact 
1. some contact details 1 
2. some contact details 2 
+0

不幸的是,OP已经说过,除了订单/付款/联系人之外,还有很多其他的元素类型,所以'xsl:sort'上的选择可能是不够的。虽然为什么1,2,4?在这种情况下,一定会有1,2,3? – Flynn1179 2010-10-12 16:12:17

+0

@ Flynn1179:没有一般解决方案。这种转换产生了所需的输出。您的排序顺序('name(*)')不会:'contact' <'order' <'payment'。 2序列的强大功能是位掩码习惯。查看我的编辑以获取更多可重用的排序表达式。 – 2010-10-12 16:35:47

+0

是的,如果你需要一个特定的排序顺序,那么一个通用的解决方案是行不通的。我是在这样的假设下工作的:输出中的组的顺序并不重要(或按字母顺序)。如果是,那么我同意有必要以某种方式显式声明它,在这种情况下,''等列表可以完成这项工作,但是OP说这不是一个解决方案。 – Flynn1179 2010-10-13 08:38:34