代码之家  ›  专栏  ›  技术社区  ›  Mork0075

具有XSLT的多个键上的不同节点

  •  7
  • Mork0075  · 技术社区  · 16 年前

    我希望在多个级别上从XML中获得不同的节点。有人能给我一些提示怎么做吗?我搜索的方法(每个组的Muenchian方法)是用单个分组键和简单的层次结构来解释的。

    下面是我的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>
    

    有办法吗?提前多谢。

    1 回复  |  直到 7 年前
        1
  •  11
  •   Dimitre Novatchev    16 年前

    i.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>
    

    二。XSLT2.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>