我正在使用XSLT转换从Web派生的XML,并将其转换为表示为输出的目标xml文件。 即使尝试了很多,我仍然无法这样做,任何人都可以帮我解决这个问题。
源XML
<allocelement>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>1</alc>
<amount>4587</amount>
<code>1111</code>
</allocelement>
<alloclement>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>2</alc>
<amount>80000</amount>
<code>1111</code>
</alloclement>
<alloclement>
<hd1>875</hd1>
<hd2>455</hd2>
<hd3>455</hd3>
<alc>2</alc>
<amount>80000</amount>
<code>1112</code>
</alloclement>
输出所需
<allocelement>
<Codeheader>
<code>1111</code>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>1</alc>
<amount>4587</amount>
<alc>2</alc>
<amount>80000</amount>
</codeHeader>
<CodeHeader>
<code>1112</code>
<hd1>875</hd1>
<hd2>455</hd2>
<hd3>455</hd3>
<alc>2</alc>
<amount>80000</amount>
</CodeHeader>
</allocelement>
分组基于代码[hd1,hd2,hd3],使得其中具有相同代码和[hd1,hd2,hd3]的不同元素将被合并,并且仅显示不同的字段即。和。 我也在使用xslt 1.0。
答案 0 :(得分:0)
我认为你的输入XML有一个root
节点,它将所有节点包装起来以制作一个格式良好的文档。我还假设所有allocelement
拼写正确(你有一些拼写为alloclement
)。我还假设您要删除标记名称及其文本值标识的重复项。
使用 XSLT 1.0 :
<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="code" match="allocelement/code" use="."/>
<xsl:key name="code-sibling"
match="allocelement/*[name() != 'code']"
use="concat(parent::*/code, '|', name(), '|', .)"/>
<xsl:template match="@* | node()" name="copy">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="root/allocelement[1]"/>
</xsl:template>
<xsl:template match="allocelement">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates
select="(. | following-sibling::allocelement)/code
[generate-id() = generate-id(key('code', .)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="code">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:value-of select="."/>
<xsl:apply-templates
select="key('code', .)/(preceding-sibling::* | following-sibling::*)
[generate-id() =
generate-id(key('code-sibling', concat(parent::*/code, '|', name(), '|', .))[1])]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
适用于:
<root>
<allocelement>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>1</alc>
<amount>4587</amount>
<code>1111</code>
</allocelement>
<allocelement>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>2</alc>
<amount>80000</amount>
<code>1111</code>
</allocelement>
<allocelement>
<hd1>875</hd1>
<hd2>455</hd2>
<hd3>455</hd3>
<alc>2</alc>
<amount>80000</amount>
<code>1112</code>
</allocelement>
</root>
产生
<root>
<allocelement>
<code>1111<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>1</alc>
<amount>4587</amount>
<alc>2</alc>
<amount>80000</amount>
</code>
<code>1112<hd1>875</hd1>
<hd2>455</hd2>
<hd3>455</hd3>
<alc>2</alc>
<amount>80000</amount>
</code>
</allocelement>
</root>
这是它的工作原理。默认路由是identity transform。我们首先中断allocelement
并将转换发送到不同的路径。我们让它复制唯一的code
元素(由元素的文本值标识的唯一性)及其值,然后在共享相同值的所有code
个节点的所有前/后兄弟节点上应用模板。这些将是code
节点的子节点。然后,我们为那些唯一的兄弟姐妹调用身份变换(属于相同值的code
,具有相同的名称和相同的文本值。)
一个注意事项。 MIXED content永远不是一个好主意。看看你是否真的需要在code
下混合文本值和子节点。
使用 XSLT 2.0 ,您可以远离generate-id()
:
<xsl:template match="allocelement">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="(. | following-sibling::allocelement)/code" group-by=".">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="code">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:value-of select="."/>
<xsl:for-each-group select="key('code', .)/(preceding-sibling::* | following-sibling::*)"
group-by="concat(parent::*/code, '|', name(), '|', .)">
<xsl:apply-templates select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
答案 1 :(得分:0)
一个明显更短更简单的XSLT 1.0解决方案:
<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="kCodeByVal" match="code" use="."/>
<xsl:template match="/*">
<allocelement>
<xsl:apply-templates/>
</allocelement>
</xsl:template>
<xsl:template match="allocelement"/>
<xsl:template match=
"allocelement
[generate-id(code) = generate-id(key('kCodeByVal', code)[1])]">
<code><xsl:value-of select="code"/>
<xsl:copy-of select=
"node()[not(self::code)]
| key('kCodeByVal', code)/../*[self::alc or self::amount]"/>
</code>
</xsl:template>
</xsl:stylesheet>
将此转换应用于以下XML文档(包装提供的XML片段的单个顶部元素):
<t>
<allocelement>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>1</alc>
<amount>4587</amount>
<code>1111</code>
</allocelement>
<allocelement>
<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>2</alc>
<amount>80000</amount>
<code>1111</code>
</allocelement>
<allocelement>
<hd1>875</hd1>
<hd2>455</hd2>
<hd3>455</hd3>
<alc>2</alc>
<amount>80000</amount>
<code>1112</code>
</allocelement>
</t>
产生了想要的正确结果:
<allocelement>
<code>1111<hd1>12</hd1>
<hd2>14</hd2>
<hd3>87</hd3>
<alc>1</alc>
<amount>4587</amount>
<alc>2</alc>
<amount>80000</amount>
</code>
<code>1112<hd1>875</hd1>
<hd2>455</hd2>
<hd3>455</hd3>
<alc>2</alc>
<amount>80000</amount>
</code>
</allocelement>
解释:正确使用 Muenchian grouping method 。
<强> II。 XSLT 2.0解决方案 - 再次更短,更重要的是 - 语法和语义正确:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<allocelement>
<xsl:for-each-group select="*" group-by="code">
<code><xsl:value-of select="code"/>
<xsl:sequence select=
"node()[not(self::code)]
| current-group()/*[self::alc or self::amount]"/>
</code>
</xsl:for-each-group>
</allocelement>
</xsl:template>
</xsl:stylesheet>
更新:OP已经更改了outpu的要求。
以下是相应的修改后的XSLT 1.0解决方案:
<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="kCodeByVal" match="code" use="."/>
<xsl:template match="/*">
<allocelement>
<xsl:apply-templates/>
</allocelement>
</xsl:template>
<xsl:template match="allocelement"/>
<xsl:template match=
"allocelement
[generate-id(code) = generate-id(key('kCodeByVal', code)[1])]">
<codeHeader>
<code><xsl:value-of select="code"/></code>
<xsl:copy-of select=
"node()[not(self::code)]
| key('kCodeByVal', code)/../*[self::alc or self::amount]"/>
</codeHeader>
</xsl:template>
</xsl:stylesheet>