XSLT:从平面到分层结构

时间:2014-10-22 15:06:03

标签: xslt

我需要转换像这样的平面结构

 <root>
    <H>1</H>
    <I>1-1</I>
    <I>1-2</I>
    <I>1-3</I>
    <H>2</H>
    <I>2-1</I>
    <I>2-2</I>
  </root>

就像这样

   <root>
        <H>
           1
           <I>1-1</I>
           <I>1-2</I>
           <I>1-3</I>
        </H>
        <H>
           2
           <I>2-1</I>
           <I>2-2</I>
        </H>
     </root>

我正在以这种方式在源结构上尝试每种方法

<?xml version="1.0" encoding="utf-8"?>
 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
 >
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="root">
       <root>
          <xsl:for-each select="child::node()">
             <xsl:if test="self::H">
                <H>
                   <xsl:copy-of select="@* | node()"/>
                   <xsl:call-template name="copyI"/>
                </H>
             </xsl:if>
          </xsl:for-each>
       </root>
    </xsl:template>

    <xsl:template name="copyI">
       <xsl:for-each select="following-sibling::node()">
          <xsl:choose>

             <xsl:when test="self::H">
                <!-- Should be fantastic to exit from the loop! -->
             </xsl:when>

             <xsl:when test="self::I"
                <I>
                   <xsl:copy-of select="@* | node()"/>
                </I>
                <xsl:text>&#13;</xsl:text>
             </xsl:when>

          </xsl:choose>
       </xsl:for-each>
    </xsl:template>

 </xsl:stylesheet>

不幸的是,我能达到的最佳结果是

<root>
    <H>
       1
       <I>1-1</I>
       <I>1-2</I>
       <I>1-3</I>
       <I>2-1</I>     wrong!
       <I>2-2</I>     wrong!
    </H>
    <H>
       2
       <I>2-1</I>
       <I>2-2</I>
    </H>
 </root>

H1下的<I>2-1</I><I>2-2</I>错误。

问题在于,当找到并且H时,我无法从模板copyI中逃脱  由于源结构扁平(所有兄弟姐妹),我害怕a  递归copyI没有帮助。

有什么建议吗?

非常感谢。  尼古拉

如何扩展您的解决方案?

托马拉克,非常感谢你的帮助。 真的,我的需求有点复杂,你的解决方案也不具备可扩展性。 在我的源代码中,可选择提供<C>第三级,分层依赖于<I>

<root>
   <H>1</H>
   <I>1-1</I>
   <C>1-1-1</C>
   <I>1-2</I>
   <C>1-2-1</C>
   <I>1-3</I>
   <H>2</H>
   <I>2-1</I>
   <I>2-2</I>
   <C>2-2-1</C>
</root>

,预期输出为

<root>
   <H>1
    <I>1-1<C>1-1-1</C></I>
    <I>1-2<C>1-2-1</C></I>
    <I>1-3</I>
   </H>
   <H>2
    <I>2-1</I>
    <I>2-2<C>2-2-1</C></I>
   </H>
</root>

再次感谢 尼古拉

1 个答案:

答案 0 :(得分:0)

这很简单:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
  exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="root">
    <xsl:copy>
      <xsl:apply-templates select="H" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="H">
    <xsl:variable name="myId" select="generate-id()" />
    <xsl:copy>
      <xsl:copy-of select="text()[normalize-space() != '']" />
      <xsl:copy-of select="
        following-sibling::I[generate-id(preceding-sibling::H[1]) = $myId]
      " />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

对于每个<H>,您需要复制{em>直接位于 <I>之前的<H>个元素与当前元素相同的元素(在XSLT中建立节点标识)通过比较generate-id())的结果。

输出:

<root>
  <H>1<I>1-1</I><I>1-2</I><I>1-3</I></H>
  <H>2<I>2-1</I><I>2-2</I></H>
</root>

缩进取决于您使用的XSLT处理器,但由于无关紧要的空白是......无关紧要,因此您不必过于担心。


如果您愿意,可以使用XSL密钥实现相同的目的:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
  exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes" />

  <xsl:key name="kI" match="I" use="generate-id(preceding-sibling::H[1])" />

  <xsl:template match="root">
    <xsl:copy>
      <xsl:apply-templates select="H" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="H">
    <xsl:copy>
      <xsl:copy-of select="text()[normalize-space() != '']" />
      <xsl:copy-of select="key('kI', generate-id())" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

密钥可以提高大型输入文档的性能。