XSLT将顺序XML转换为分层XML

时间:2012-12-06 15:47:36

标签: xslt biztalk xslt-1.0 biztalk-2009 biztalk-mapper

我需要将顺序XML节点列表转换为层次结构,但我遇到了一些特定于XSLT的知识差距。 输入XML包含文章,颜色和大小。在下面的示例中,'Record1'是一篇文章,'Record2'代表一种颜色,'Record3'代表大小。颜色和大小(record2和record3)元素的数量可以变化。

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

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

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

有人能把我推向正确的方向吗?

3 个答案:

答案 0 :(得分: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>

答案 1 :(得分: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>

并且每个级别都类似。这个解决方案可能比其他解决方案更快更简洁,但在您习惯之前它可能有点“令人费解”。理解它的关键是,在每个级别,你首先处理第一个“逻辑子” - 也就是平面序列中的下一个元素,前提是它在比当前元素更深的层次上;然后你处理下一个“逻辑兄弟” - 平面序列中与当前序列处于同一级别的下一个元素。当然,对于可能存在缺失级别或级别编号由属性而不是元素名称指示的情况,可以调整解决方案。它甚至可以适用于事先不知道级别数的情况:您只需使用一个规则,其中“第一逻辑子”和“下一个逻辑兄弟”的选择被适当地参数化。

答案 2 :(得分: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的密钥将Record2color)元素集合表示为Record1article)逻辑父项的函数

  2. 同样,名为kSizes的密钥将Record3size)元素集合表示为Record2color)的函数合乎逻辑的父母。