我想使用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;分隔的元素名称。客户需要这样,以便他们可以直接将所需的部分绑定到相应的网格。
答案 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}})获取一系列名称标记(例如_
,fruits
,apple
)并使用第一个标记(例如red
)作为第一次调用的分组键(fruits
为[$index]
谓词),然后是第二次调用的第二个令牌(例如[1]
),依此类推。当组中只有一个元素时,就会停止递归,因为我推断这是一个条件,例如, apple
不会进一步分裂。