Отдельные узлы на нескольких ключах с XSLT

я хотел бы получить отдельные узлы из моего xml на нескольких уровнях. Может кто-нибудь дать мне несколько советов, как это сделать? Методы, которые я гуглил (метод Мюнхена, для каждой группы), были объяснены с помощью отдельных ключей группировки и простой иерархии.

Вот пример моего 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>

Есть ли способ сделать это? Заранее большое спасибо.

7
задан Jay 21 December 2015 в 19:46
поделиться

1 ответ

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>

II. Решение 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>
11
ответ дан 6 December 2019 в 22:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: