Многоуровневый XML в плоский XML с использованием XSLT - PullRequest
1 голос
/ 20 марта 2012

Я конвертирую одни и те же элементы нескольких тегов в разные записи.В этом я использую XSLT.Мне нужна помощь в XSLT.я получаю первый результат тега правильно, я не знаю, почему вторые теги не получают.

Объяснение :: * В первом CD :: Одиночный заголовок для многих названий.Я хочу разделить это на разные теги на основе заголовка.Во втором CD :: Первый заголовок первого исполнителя и второй заголовок второго исполнителя.Мне это нужно также в разных (двух) тегах CD, основанных как на названии, так и на исполнителе. [Первый исполнитель, первый исполнитель и второй заголовок, второй исполнитель]. *

Мой исходный XML похож наСледующий ::

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<catalog>
    <cd>
        <title>Burlesque</title>        
        <title>Empire</title>
        <title>Emp</title>
        <title>Em</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Hide your</title>
        <title>heart</title>
        <artist>Bonnie</artist>
        <artist> Tyler</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
    </cd>
    <cd>
        <title/>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <company>RCA</company>
        <price>9.90</price>
        <year>1982</year>
    </cd>
</catalog>

XSLT ::

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  >
    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="k" match="title" use="text()"/>
    <xsl:key name="l" match="artist" use="text()"/>
            <xsl:template match="/">
        <catalog>
            <xsl:apply-templates select="//cd/title | artist[not(node() = preceding-sibling::node())]"/>
        </catalog>
    </xsl:template>
    <xsl:template match="//cd">
        <xsl:param name="title" select="title"/>
        <xsl:param name="artist" select="artist"/>
        <cd>
            <xsl:copy-of select="key('k', $title)[not(node() = preceding-sibling::node())]"/>
            <xsl:copy-of select="key('l', $artist)[not(node() = preceding-sibling::node())]"/>
            <xsl:copy-of select="./*[name() != 'title' and 'artist']"/>
        </cd>
    </xsl:template>
    <xsl:template match="title">
            <xsl:apply-templates select="..">
            <xsl:with-param name="title" select="."/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

Что я получаю ::

<?xml version="1.0" encoding="utf-8"?>
<catalog>
  <cd>
    <title>Burlesque</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Empire</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Emp</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Em</title>
    <artist>Bob Dylan</artist>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your</title>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <title>heart</title>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <artist>Bonnie</artist>
    <artist> Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <artist>Dolly Parton</artist>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <company>RCA</company>
    <price>9.90</price>
    <year>1982</year>
  </cd>
 </catalog>

Что мне нужно ::

<?xml version="1.0" encoding="utf-8"?>
<catalog>
  <cd>
    <title>Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Empire</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Emp</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Em</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
  <cd>
    <title>Hide your</title>
    <artist>Bonnie</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <title>heart</title>
    <artist> Tyler</artist>
    <country>UK</country>
    <company>CBS Records</company>
    <price>9.90</price>
    <year>1988</year>
  </cd>
  <cd>
    <artist>Dolly Parton</artist>
    <artist>Dolly Parton</artist>
    <country>USA</country>
    <company>RCA</company>
    <price>9.90</price>
    <year>1982</year>
  </cd>
</catalog>

Может ли кто-нибудь помочь мне в этом ???

1 Ответ

1 голос
/ 20 марта 2012

Это приложение преобразования "общего измельчения 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:strip-space elements="*"/>

     <xsl:param name="pLeafNodes" select="/*/*/title"/>

     <xsl:template match="/">
      <xsl:variable name="vrtfResult">
        <xsl:call-template name="StructRepro"/>
      </xsl:variable>

      <catalog>
       <xsl:copy-of select="ext:node-set($vrtfResult)/catalog/node()"/>
      </catalog>
     </xsl:template>

     <xsl:template name="StructRepro">
       <xsl:param name="pLeaves" select="$pLeafNodes"/>

       <xsl:for-each select="$pLeaves">
         <xsl:apply-templates mode="build" select="/*">
          <xsl:with-param name="pChild" select="."/>
          <xsl:with-param name="pLeaves" select="$pLeaves"/>
         </xsl:apply-templates>
       </xsl:for-each>
     </xsl:template>

      <xsl:template mode="build" match="node()|@*">
          <xsl:param name="pChild"/>
          <xsl:param name="pLeaves"/>

         <xsl:copy>
           <xsl:apply-templates mode="build" select="@*"/>

           <xsl:variable name="vLeafChild" select=
             "*[count(.|$pChild) = count($pChild)]"/>

           <xsl:choose>
            <xsl:when test="$vLeafChild">
             <xsl:apply-templates mode="build"
                 select="$vLeafChild
                        |
                          node()[not(count(.|$pLeaves) = count($pLeaves))]">
                 <xsl:with-param name="pChild" select="$pChild"/>
                 <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
             <xsl:apply-templates mode="build" select=
             "node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                    or
                     .//*[count(.|$pChild) = count($pChild)]
                    ]
             ">

                 <xsl:with-param name="pChild" select="$pChild"/>
                 <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
            </xsl:otherwise>
           </xsl:choose>
         </xsl:copy>
     </xsl:template>
     <xsl:template match="text()"/>
</xsl:stylesheet>

, когда это преобразование применяется к предоставленному XML-документу :

<catalog>
    <cd>
        <title>Burlesque</title>
        <title>Empire</title>
        <title>Emp</title>
        <title>Em</title>
        <artist>Bob Dylan</artist>
        <country>USA</country>
        <company>Columbia</company>
        <price>10.90</price>
        <year>1985</year>
    </cd>
    <cd>
        <title>Hide your</title>
        <title>heart</title>
        <artist>Bonnie</artist>
        <artist> Tyler</artist>
        <country>UK</country>
        <company>CBS Records</company>
        <price>9.90</price>
        <year>1988</year>
    </cd>
    <cd>
        <title/>
        <artist>Dolly Parton</artist>
        <country>USA</country>
        <company>RCA</company>
        <price>9.90</price>
        <year>1982</year>
    </cd>
</catalog>

требуемыйполучен правильный результат :

<catalog>
   <cd>
      <title>Burlesque</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Empire</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Emp</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Em</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Hide your</title>
      <artist>Bonnie</artist>
      <artist> Tyler</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title>heart</title>
      <artist>Bonnie</artist>
      <artist> Tyler</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title/>
      <artist>Dolly Parton</artist>
      <country>USA</country>
      <company>RCA</company>
      <price>9.90</price>
      <year>1982</year>
   </cd>
</catalog>

Обновление : ОП изменил свой вопрос, и он просит, чтобы для дисков с более чем одним артистом был дополнительный сплитпо художнику.

Вот решение - это двухпроходная обработка .На первом проходе документ преобразуется в catalog, в котором каждый cd имеет один artist.Тогда второй проход - это решение, которое я уже дал выше:

<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:strip-space elements="*"/>

         <xsl:template match="/">

          <xsl:variable name="vrtfPass1">
            <xsl:apply-templates mode="pass1"/>
          </xsl:variable>

          <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>

          <xsl:variable name="pLeafNodes" select="$vPass1/*/*/title"/>

          <xsl:variable name="vrtfResult">
            <xsl:call-template name="StructRepro">
              <xsl:with-param name="pLeaves" select="$pLeafNodes"/>
            </xsl:call-template>
          </xsl:variable>

          <catalog>
            <xsl:copy-of select="ext:node-set($vrtfResult)/catalog/node()"/>
          </catalog>
         </xsl:template>

         <xsl:template name="StructRepro">
           <xsl:param name="pLeaves"/>

           <xsl:variable name="vDoc" select=
               "$pLeaves[1]/ancestor::node()[last()]"/>

           <xsl:for-each select="$pLeaves">
             <xsl:apply-templates mode="build" select="$vDoc/*">
              <xsl:with-param name="pChild" select="."/>
              <xsl:with-param name="pLeaves" select="$pLeaves"/>
             </xsl:apply-templates>
           </xsl:for-each>
         </xsl:template>

          <xsl:template mode="build" match="node()|@*">
              <xsl:param name="pChild"/>
              <xsl:param name="pLeaves"/>

             <xsl:copy>
               <xsl:apply-templates mode="build" select="@*"/>

               <xsl:variable name="vLeafChild" select=
                 "*[count(.|$pChild) = count($pChild)]"/>

               <xsl:choose>
                <xsl:when test="$vLeafChild">
                 <xsl:apply-templates mode="build"
                     select="$vLeafChild
                            |
                              node()[not(count(.|$pLeaves) = count($pLeaves))]">
                     <xsl:with-param name="pChild" select="$pChild"/>
                     <xsl:with-param name="pLeaves" select="$pLeaves"/>
                 </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                 <xsl:apply-templates mode="build" select=
                 "node()[not(.//*[count(.|$pLeaves) = count($pLeaves)])
                        or
                         .//*[count(.|$pChild) = count($pChild)]
                        ]
                 ">

                     <xsl:with-param name="pChild" select="$pChild"/>
                     <xsl:with-param name="pLeaves" select="$pLeaves"/>
                 </xsl:apply-templates>
                </xsl:otherwise>
               </xsl:choose>
             </xsl:copy>
         </xsl:template>

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

 <xsl:template match="cd[artist[2]]" mode="pass1">
  <xsl:for-each select="artist">
   <xsl:apply-templates select=".." mode="singleArtist">
     <xsl:with-param name="pArtistPos" select="position()"/>
   </xsl:apply-templates>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="cd" mode="singleArtist">
   <xsl:param name="pArtistPos"/>

   <cd>
     <xsl:apply-templates mode="pass1" select=
        "title[position() = $pArtistPos]"/>
     <xsl:apply-templates mode="pass1" select=
         "artist[position() = $pArtistPos]"/>
     <xsl:apply-templates mode="pass1" select=
         "node()[not(self::title or self::artist)]"/>
   </cd>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

Когда это преобразование применяется к тому же XML-документу (см. Выше), результат теперь удовлетворяет дополнительным требованиям :

<catalog>
   <cd>
      <title>Burlesque</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Empire</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Emp</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Em</title>
      <artist>Bob Dylan</artist>
      <country>USA</country>
      <company>Columbia</company>
      <price>10.90</price>
      <year>1985</year>
   </cd>
   <cd>
      <title>Hide your</title>
      <artist>Bonnie</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title>heart</title>
      <artist> Tyler</artist>
      <country>UK</country>
      <company>CBS Records</company>
      <price>9.90</price>
      <year>1988</year>
   </cd>
   <cd>
      <title/>
      <artist>Dolly Parton</artist>
      <country>USA</country>
      <company>RCA</company>
      <price>9.90</price>
      <year>1982</year>
   </cd>
</catalog>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...