【问题标题】:Multi Level XML to Flat XML using XSLT使用 XSLT 将多级 XML 转换为平面 XML
【发布时间】:2012-04-04 21:48:34
【问题描述】:

我正在将相同的多标签元素转换为不同的记录。 在这我使用 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>

谁能帮帮我???

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    这是 "generic XML shredding" transformation 的一个应用程序,其中参数设置为所需的叶节点,并进行少量后处理以从结果中删除两个顶部包装节点并重新换行:

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

    更新:OP 改变了他的问题,他要求对于有多个艺术家的 CD,应该按艺术家进行额外的拆分。

    这是解决方案——它是两次处理。在第一遍中,文档被转换为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>
    

    【讨论】:

    • 一次请看 标签。它来了两次。 隐藏你的 Bonnie TylerUKCBS Records9.90 1988heart Bonnie TylerUKCBS Records9.901988
    • @ram.bi:我明白了,所以你想要的比简单的粉碎还要复杂,不同的艺术家需要结合不同的标题。但随后您必须编辑问题并说明应将哪个艺术家与哪个标题相匹配。可能两位艺术家都唱同一首歌(我假设),或者两位艺术家都唱两首歌,或者第一位艺术家唱第二首歌(想象艺术家和歌曲按名称字母顺序排列。请编辑问题并指定确切的规则。
    • 您好 Dimitre,感谢您的回复,我添加了解释,请帮我正确处理。
    • @ram.bi:当然,请查看我在此答案末尾的更新:)
    • 当时我在 SQL Server Integration Services XML 任务中使用 XML 和 XSLT,我收到如下错误::找不到实现前缀“ext”的脚本或外部对象。我对 XSLT 了解不多,请帮助我吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多