Преобразование входного XML с помощью XSLT в другой XML

Я новичок и хочу изучить XSLT. Я столкнулся с проблемой преобразования входного XML-файла в другой XML-файл с помощью XSLT.

Мой входной XML-файл:

<album>
<album_num>hi.hello</album_num>
<album_name>Cocktail</album_name>
</album>
<album>
<album_num>hey.hello</album_num>
<album_name>Mocktail</album_name>
</album>
<album>
<album_num>hey.mello</album_num>
<album_name>Monkeytail</album_name>
</album>
<album>
<album_num>hey.yellow</album_num>
<album_name>Donkeytail</album_name>
</album>
<album>
<album_num>swallow</album_num>
<album_name>abc</album_name>
</album>

Я хотел бы получить выходной файл XML следующим образом:

<album>
<album_num>
<hi>
<hello>cocktail</hello>
</hi>
</album_num>
<album_num>
<hey>
<hello>MockTail</hello>
<mello>Monkeytail</mello>
<yellow>Donkeytail</yellow>
</hey>
</album_num>
<album_num>
<swallow>abc</swallow>
</album_num>
</album>

Я попробовал первую часть, создав переменные, но у меня возникла проблема с объединением похожих элементов в один элемент. Любой код может помочь мне научиться.

Мой код:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<album>
<xsl:variable name="fstval" select='substring-before(//album/album_num,".")'/>
<xsl:variable name="secval" select='substring-after(//album/album_num,".")'/>
<xsl:variable name="valtoappend" select='//album/album_name'/>
<album_num>
<xsl:element name="{$fstval}">
<xsl:element name="{$secval}">
<xsl:value-of select="$valtoappend"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</album_num>
</album>
</xsl:template>
</xsl:stylesheet>

person Ramana    schedule 02.07.2012    source источник


Ответы (1)


Это преобразование:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kAlbumByChildName" match="album" use="name(album_num/*[1])"/>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
    <xsl:apply-templates/>
  </xsl:variable>

  <xsl:apply-templates mode="pass2" select=
  "ext:node-set($vrtfPass1)/*
           [generate-id()
           =
            generate-id(key('kAlbumByChildName', name(album_num/*[1]))[1])
           ]
  "/>
 </xsl:template>

 <xsl:template match="album">
  <album>
   <album_num>
       <xsl:element name="{substring-before(album_num, '.')}">
         <xsl:element name="{substring-after(album_num, '.')}">
           <xsl:value-of select="album_name"/>
         </xsl:element>
       </xsl:element>
   </album_num>
  </album>
 </xsl:template>

 <xsl:template match="album" mode="pass2">
  <album>
   <album_num>
        <xsl:apply-templates select="*/*[1]" mode="pass2"/>
    </album_num>
  </album>
 </xsl:template>

 <xsl:template match="album_num/*" mode="pass2">
  <xsl:copy>
   <xsl:copy-of select="key('kAlbumByChildName', name())/*/*/*"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

при применении к следующему документу (предоставленный фрагмент XML заключен в один верхний элемент, чтобы сделать его правильно сформированным XML-документом):

<t>
    <album>
        <album_num>hi.hello</album_num>
        <album_name>Cocktail</album_name>
    </album>
    <album>
        <album_num>hey.hello</album_num>
        <album_name>Mocktail</album_name>
    </album>
    <album>
        <album_num>hey.mello</album_num>
        <album_name>Monkeytail</album_name>
    </album>
    <album>
        <album_num>hey.yellow</album_num>
        <album_name>Donkeytail</album_name>
    </album>
</t>

выдает желаемый правильный результат:

<album>
   <album_num>
      <hi>
         <hello>Cocktail</hello>
      </hi>
   </album_num>
</album>
<album>
   <album_num>
      <hey>
         <hello>Mocktail</hello>
         <mello>Monkeytail</mello>
         <yellow>Donkeytail</yellow>
      </hey>
   </album_num>
</album>

Пояснение:

Это двухпроходное преобразование. Результат первого прохода::

<album>
   <album_num>
      <hi>
         <hello>Cocktail</hello>
      </hi>
   </album_num>
</album>

<album>
   <album_num>
      <hey>
         <hello>Mocktail</hello>
      </hey>
   </album_num>
</album>

<album>
   <album_num>
      <hey>
         <mello>Monkeytail</mello>
      </hey>
   </album_num>
</album>

<album>
   <album_num>
      <hey>
         <yellow>Donkeytail</yellow>
      </hey>
   </album_num>
</album>

Второй проход – стандартная мюнхенская группировка.

Обновление:

Через два дня после того, как он задал этот вопрос и получил правильный ответ, OP изменил исходный XML-документ и получил желаемый результат.

Это слегка измененное преобразование:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>

     <xsl:key name="kAlbumByChildName" match="album" use="name(album_num/*[1])"/>

     <xsl:template match="/">
      <xsl:variable name="vrtfPass1">
        <xsl:apply-templates/>
      </xsl:variable>

      <xsl:apply-templates mode="pass2" select=
      "ext:node-set($vrtfPass1)/*
               [generate-id()
               =
                generate-id(key('kAlbumByChildName', name(album_num/*[1]))[1])
               or
                not(album_num/*)
               ]
      "/>

     </xsl:template>

     <xsl:template match="album[contains(album_num, '.')]">
      <album>
       <album_num>
           <xsl:element name="{substring-before(album_num, '.')}">
             <xsl:element name="{substring-after(album_num, '.')}">
               <xsl:value-of select="album_name"/>
             </xsl:element>
           </xsl:element>
       </album_num>
      </album>
     </xsl:template>

     <xsl:template match="album">
      <album>
       <album_num>
             <xsl:element name="{album_num}">
               <xsl:value-of select="album_name"/>
             </xsl:element>
       </album_num>
      </album>
     </xsl:template>

     <xsl:template match="album" mode="pass2">
      <album>
       <album_num>
            <xsl:apply-templates select="*/*[1]" mode="pass2"/>
        </album_num>
      </album>
     </xsl:template>

     <xsl:template match="album_num/*" mode="pass2">
      <xsl:copy>
       <xsl:copy-of select="self::*[not(*)]/text()|key('kAlbumByChildName', name())/*/*/*"/>
      </xsl:copy>
     </xsl:template>
</xsl:stylesheet>

при применении к новой версии XML-документа:

<t>
    <album>
        <album_num>hi.hello</album_num>
        <album_name>Cocktail</album_name>
    </album>
    <album>
        <album_num>hey.hello</album_num>
        <album_name>Mocktail</album_name>
    </album>
    <album>
        <album_num>hey.mello</album_num>
        <album_name>Monkeytail</album_name>
    </album>
    <album>
        <album_num>hey.yellow</album_num>
        <album_name>Donkeytail</album_name>
    </album>
    <album>
        <album_num>swallow</album_num>
        <album_name>abc</album_name>
    </album>
</t>

выдает новый желаемый результат:

<album>
   <album_num>
      <hi>
         <hello>Cocktail</hello>
      </hi>
   </album_num>
</album>
<album>
   <album_num>
      <hey>
         <hello>Mocktail</hello>
         <mello>Monkeytail</mello>
         <yellow>Donkeytail</yellow>
      </hey>
   </album_num>
</album>
<album>
   <album_num>
      <swallow>abc</swallow>
   </album_num>
</album>
person Dimitre Novatchev    schedule 02.07.2012
comment
Спасибо Димитре. Я рад оценить вашу помощь для новичка, как я. - person Ramana; 02.07.2012
comment
@Ramana: Я рад, что мой ответ был вам полезен. Не могли бы вы, пожалуйста, принять его (нажав галочку рядом с ответом)? - person Dimitre Novatchev; 04.07.2012
comment
@Ramana: Пожалуйста, посмотрите обновление этого ответа - оно решает проблему, которую вы изменили. Вам не кажется, что вы должны принять этот ответ? - person Dimitre Novatchev; 04.07.2012
comment
Прошу прощения, я не знаю, как использовать этот stackoverflow. Я удалил дубликат dquestion. Спасибо за помощь. - person Ramana; 05.07.2012