使用xslt

时间:2018-01-13 11:51:44

标签: xml xslt

我想使用XSLT将一个XML中收到的数据转换为另一个XML文件。

要解析的XML数据包含" _" (下划线)。我想从这个XML生成元素,并根据从元素名称中提取的名称对它们进行分组。

要解析的XML

<data>
   <fruits_apple_red>1</fruits_apple_red>
   <animal_carnivorous_cat_tiger_white>5</animal_carnivorous_cat_tiger_white>
   <animal_reptiles_lizard>3</animal_reptiles_lizard>
   <animal_carnivorous_lion>4</animal_carnivorous_lion>
   <fruits_orange_orange>2</fruits_orange_orange>
   <animal_carnivorous_cat_hyena>6</animal_carnivorous_cat_hyena>
</data>

需要XML

<?xml version="1.0" encoding="UTF-8"?>
<data>
   <fruits>
      <apple_red>1</apple_red>
      <orange_orange>2</orange_orange>
   </fruits>
   <animal>
      <carnivorous>
         <cat>
            <tiger_white>5</tiger_white>
            <hyena>6</hyena>
         </cat>
         <lion>4</lion>
      </carnivorous>
      <reptiles_lizard>3</reptiles_lizard>
   </animal>
</data>

我是XSLT的新手,我尝试过使用XSLT字符串函数(substring)但无法以这种格式获取XML。我遇到的问题是元素名称是动态的,我们需要从元素名称中提取名称。 所以,我真的不能用名字来编写代码&#34;数据&#34;作为根元素或&#34;动物/果实&#34;作为第二级元素名称。

我们需要的另一件事是多级(比如动物)虽然我觉得这可能需要在某个阶段进行一些硬编码检查(使用xsl:if语句)。但重点是尽可能多组数据。

任何人都可以告诉我如何使用XSLT完成此操作。

注意:这是示例数据,但实际数据包含由&#34; _&#34;分隔的元素名称。客户需要这样,以便他们可以直接将所需的部分绑定到相应的网格。

1 个答案:

答案 0 :(得分:0)

我认为在XSLT 2或3中使用递归函数和xsl:for-each-group group-by可能如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="xs mf"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output indent="yes"/>

  <xsl:function name="mf:group" as="element()*">
      <xsl:param name="elements" as="element()*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group select="$elements" group-by="tokenize(local-name(), '_')[$level]">
          <xsl:choose>
              <xsl:when test="not(current-group()[2])">
                  <xsl:element name="{string-join(tokenize(local-name(), '_')[position() ge $level], '_')}">
                      <xsl:apply-templates/>
                  </xsl:element>
              </xsl:when>
              <xsl:otherwise>
                  <xsl:element name="{current-grouping-key()}">
                      <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
                  </xsl:element>
              </xsl:otherwise>
          </xsl:choose>
      </xsl:for-each-group>
  </xsl:function>

  <xsl:template match="data">
      <xsl:copy>
          <xsl:sequence select="mf:group(*, 1)"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

导致(见http://xsltfiddle.liberty-development.net/gWcDMeh

<data>
   <fruits>
      <apple_red>1</apple_red>
      <orange_orange>2</orange_orange>
   </fruits>
   <animal>
      <carnivorous>
         <cat>
            <tiger_white>5</tiger_white>
            <hyena>6</hyena>
         </cat>
         <lion>4</lion>
      </carnivorous>
      <reptiles_lizard>3</reptiles_lizard>
   </animal>
</data>

所以它有正确的元素,虽然订单不同,我无法从你的帖子推断出什么决定了订单。然而,添加一些其他排序并不是很困难,如果有必要,可以将函数的结果推送到另一个建立所需顺序的模式。

至于解释:该函数将*个元素的子元素data作为初始输入,并通过标记for-each-group递归使用local-name()(例如{分隔符fruits_apple_red上的每个元素{1}})获取一系列名称标记(例如_fruitsapple)并使用第一个标记(例如red)作为第一次调用的分组键(fruits[$index]谓词),然后是第二次调用的第二个令牌(例如[1]),依此类推。当组中只有一个元素时,就会停止递归,因为我推断这是一个条件,例如, apple不会进一步分裂。