8

我需要将顺序 XML 节点列表转换为层次结构,但我遇到了一些 XSLT 特定的知识差距。输入 XML 包含文章、颜色和尺寸。在下面的示例中,“Record1”是一篇文章,“Record2”代表一种颜色,“Record3”是尺寸。颜色和大小(记录 2 和记录 3)元素的数量可能会有所不同。

<root>
 <Record1>...</Record1>
 <Record2>...</Record2>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record2>...</Record2>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record1>...</Record1>
 <Record2>...</Record2>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record2>...</Record2>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record3>...</Record3>
 <Record3>...</Record3>
</root> 

所有字段都在同一层级上,但我仍然必须创建这个结构作为输出:

<root>
 <article>              -> Record1
  <color>               -> Record2
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
  </color>
  <color>               -> Record2
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
  </color>
 </article>
 <article>              -> Record1
  <color>               -> Record2
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
  </color>
  <color>               -> Record2
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
   <size>...</size>     -> Record3
  </color>
 </article>
</root>

我尝试按顺序迭代节点,但例如,在处理“颜色”(=record2) 节点时,“文章”(=record1) 节点标签需要保持未关闭状态。处理未关闭“颜色”的“大小”(=record3) 的计数相同,但 XSLT 不允许这样做。我的下一个想法是为每篇文章、颜色和大小级别调用一个模板,但我不知道如何选择当前“record2”和“record1”表示的下一篇文章之间的所有“record3”节点。

我对 XSLT 版本也有限制,因为我需要在仅支持 XSLT 1.0 的 BizTalk Server 中进行此转换

有人可以将我推向正确的方向吗?

4

3 回答 3

3

这是一个 XSLT 1.0 选项。我不确定你想对 Record1 和 Record2 的值做什么,所以我把它们放在一个val属性中。

XML 输入

<root>
    <Record1>article1</Record1>
    <Record2>color1</Record2>
    <Record3>size1</Record3>
    <Record3>size2</Record3>
    <Record2>color2</Record2>
    <Record3>size3</Record3>
    <Record3>size4</Record3>
    <Record3>size5</Record3>
    <Record3>size6</Record3>
    <Record1>article2</Record1>
    <Record2>color3</Record2>
    <Record3>size7</Record3>
    <Record3>size8</Record3>
    <Record2>color4</Record2>
    <Record3>size9</Record3>
    <Record3>size10</Record3>
    <Record3>size11</Record3>
    <Record3>size12</Record3>
</root>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:apply-templates select="Record1"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Record1">
        <article val="{.}">
            <xsl:apply-templates select="following-sibling::Record2[generate-id(preceding-sibling::Record1[1])=generate-id(current())]"/>
        </article>
    </xsl:template>

    <xsl:template match="Record2">
        <color val="{.}">
            <xsl:apply-templates select="following-sibling::Record3[generate-id(preceding-sibling::Record2[1])=generate-id(current())]"/>
        </color>
    </xsl:template>

    <xsl:template match="Record3">
        <size>
            <xsl:value-of select="."/>
        </size>
    </xsl:template>

</xsl:stylesheet>

XML 输出

<root>
   <article val="article1">
      <color val="color1">
         <size>size1</size>
         <size>size2</size>
      </color>
      <color val="color2">
         <size>size3</size>
         <size>size4</size>
         <size>size5</size>
         <size>size6</size>
      </color>
   </article>
   <article val="article2">
      <color val="color3">
         <size>size7</size>
         <size>size8</size>
      </color>
      <color val="color4">
         <size>size9</size>
         <size>size10</size>
         <size>size11</size>
         <size>size12</size>
      </color>
   </article>
</root>
于 2012-12-06T17:30:39.607 回答
3

DevNull 和 Novatchev 的解决方案都是可行的。第三种解决方案,在我看来最优雅的一个,是使用“兄弟递归”。我不会给出完整的代码,但你从一个根的模板规则开始,如下所示:

<xsl:template match="root">
  <xsl:apply-templates select="*[1]"/>
</xsl:template>

然后为不同的“级别”设置模板规则,如下所示:

<xsl:template match="record1">
  <section1>
    <xsl:apply-templates select="following-sibling::*[1][self::record2]"/>
  </section1>
  <xsl:apply-templates select="following-sibling::record1[1]"/>
</xsl:template>

每个级别也是如此。这个解决方案可能比其他解决方案更快、更简洁,尽管在你习惯之前它可能有点“令人费解”。理解它的关键是,在每一层,你首先处理第一个“逻辑子”——即平面序列中的下一个元素,前提是它比当前元素更深;然后你处理下一个“逻辑兄弟”——平面序列中与当前元素处于同一级别的下一个元素。当然,该解决方案可以适用于可能缺少级别或级别编号由属性而不是元素名称指示的情况。它甚至可以适用于事先不知道级别数的情况:您只需使用一个规则,其中“

于 2012-12-07T09:28:31.033 回答
2

这是使用键的更简单且可能更有效的转换:

<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="kColors" match="Record2"
          use="generate-id(preceding-sibling::Record1[1])"/>

 <xsl:key name="kSizes" match="Record3"
          use="generate-id(preceding-sibling::Record2[1])"/>

 <xsl:template match="/*">
  <root>
    <xsl:apply-templates select="Record1"/>
  </root>
 </xsl:template>

 <xsl:template match="Record1">
   <article>
     <xsl:apply-templates select="key('kColors', generate-id())"/>
   </article>
 </xsl:template>

 <xsl:template match="Record2">
  <color>
     <xsl:apply-templates select="key('kSizes', generate-id())"/>
  </color>
 </xsl:template>

 <xsl:template match="Record3">
  <size><xsl:apply-templates/></size>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时:

<root>
    <Record1>article1</Record1>
    <Record2>color1</Record2>
    <Record3>size1</Record3>
    <Record3>size2</Record3>
    <Record2>color2</Record2>
    <Record3>size3</Record3>
    <Record3>size4</Record3>
    <Record3>size5</Record3>
    <Record3>size6</Record3>
    <Record1>article2</Record1>
    <Record2>color3</Record2>
    <Record3>size7</Record3>
    <Record3>size8</Record3>
    <Record2>color4</Record2>
    <Record3>size9</Record3>
    <Record3>size10</Record3>
    <Record3>size11</Record3>
    <Record3>size12</Record3>
</root>

产生了想要的正确结果:

<root>
   <article>
      <color>
         <size>size1</size>
         <size>size2</size>
      </color>
      <color>
         <size>size3</size>
         <size>size4</size>
         <size>size5</size>
         <size>size6</size>
      </color>
   </article>
   <article>
      <color>
         <size>size7</size>
         <size>size8</size>
      </color>
      <color>
         <size>size9</size>
         <size>size10</size>
         <size>size11</size>
         <size>size12</size>
      </color>
   </article>
</root>

说明

  1. 命名的键将( ) 元素kColors的集合表示为其( ) 逻辑父元素的函数。Record2colorRecord1article

  2. 类似地,被命名的键将( ) 元素kSizes的集合表示为它们( ) 逻辑父元素的函数。Record3sizeRecord2color

于 2012-12-07T04:56:19.903 回答