xslt - 使用 XSLT 的多个键上的不同节点

标签 xslt grouping distinct

我想在多个级别上从我的 xml 中获取不同的节点。谁能给我一些提示如何做到这一点?我在 google 上搜索的方法(Muenchian 方法,for-each-group)用单个分组键和简单的层次结构进行了解释。

这是我的 xml 的示例:

<persons>
 <person>
  <name>Tom</name>
  <age>20</age>
  <mails>
   <mail>x@test.com</mail>
   <mail>y@test.com</mail>
  </mails>
 </person>
 <person>
  <name>Tom</name>
  <age>20</age>
  <mails>
   <mail>y@test.com</mail>
   <mail>z@test.com</mail>
  </mails>
 </person> 
</persons>

我想要基于姓名和年龄的不同人员节点,以及一组不同的邮件节点。因此,对于示例所需的输出将是:
<persons>
 <person>
  <name>Tom</name>
  <age>20</age>
  <mails>
   <mail>x@test.com</mail>
   <mail>y@test.com</mail>
   <mail>z@test.com</mail>
  </mails>
 </person>
</persons>

有没有办法做到这一点?非常感谢。

最佳答案

一、XSLT 1.0解决方案 :

本次改造 :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kPersByNameAndAge" match="person"
  use="concat(name, '+', age)"/>

 <xsl:key name="kmailByNameAndAge" match="mail"
  use="concat(../../name, '+', ../../age)"/>

 <xsl:key name="kmailByNameAndAgeAndVal" match="mail"
  use="concat(../../name, '+', ../../age, '+', .)"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <persons>
   <xsl:apply-templates select=
   "person[generate-id()
          =
           generate-id(key('kPersByNameAndAge',
                          concat(name, '+', age)
                          )
                           [1]
                      )
           ]
   "/>
  </persons>
 </xsl:template>

 <xsl:template match="mails">
  <mails>
   <xsl:apply-templates select=
    "key('kmailByNameAndAge', concat(../name, '+', ../age))
        [generate-id()
        =
         generate-id(key('kmailByNameAndAgeAndVal',
                         concat(../../name, '+', ../../age, '+', .)
                         )
                          [1]
                     )
         ]
    "/>
  </mails>
 </xsl:template>
</xsl:stylesheet>

当应用于提供的 XML 文档时 :
<persons>
 <person>
  <name>Tom</name>
  <age>20</age>
  <mails>
   <mail>x@test.com</mail>
   <mail>y@test.com</mail>
  </mails>
 </person>
 <person>
  <name>Tom</name>
  <age>20</age>
  <mails>
   <mail>y@test.com</mail>
   <mail>z@test.com</mail>
  </mails>
 </person>
</persons>

产生想要的、正确的结果 :
<persons>
    <person>
        <name>Tom</name>
        <age>20</age>
        <mails>
            <mail>x@test.com</mail>
            <mail>y@test.com</mail>
            <mail>z@test.com</mail>
        </mails>
    </person>
</persons>

二、 XSLT 2.0 解决方案
<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kmailByNameAndAge" match="mail"
  use="concat(../../name, '+', ../../age)"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/*">
  <persons>
   <xsl:for-each-group select="person" group-by="concat(name, '+', age)">
     <xsl:apply-templates select="."/>
   </xsl:for-each-group>
  </persons>
 </xsl:template>

 <xsl:template match="mails">
  <mails>
   <xsl:for-each-group select=
    "key('kmailByNameAndAge', concat(../name, '+', ../age))"
    group-by="concat(../../name, '+', ../../age, '+', .)"
    >
     <xsl:apply-templates select="."/>
   </xsl:for-each-group>
  </mails>
 </xsl:template>
</xsl:stylesheet>

关于xslt - 使用 XSLT 的多个键上的不同节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3557405/

相关文章:

xml - 在XSLT中解析字符串

javascript - 未定义 : undefined error when calling XSLTProcessor. 原型(prototype).importStylesheet

mysql - 如何从外键中选择一行而不是更多? - - 通过...分组 - -

mysql - SQL 查询返回 "Operand should contain 1 column(s)"

ios - Group Array 在 Swift 中向 UITableView 引入部分

linq - 如何将自定义比较器与 Linq Distinct 方法一起使用?

java - 为什么插入符号在此正则表达式中是非法字符?

xml - XSLT 转换取决于父项值

.net - 在代码中执行结果集分组,而不是在数据库级别

ruby-on-rails - 按周/月/等和 ActiveRecord 分组?