我需要使用XSLT转换XML文件,这项任务有点棘手。
我的名称为attr_1000_a
的属性,而数字和后缀是动态的,因此attr_2000_b
也是有效的。
此外,还有<row>
个组合相关数据的元素。我需要对它们进行转换,以便将同等数量的属性(即attr_1000_a
和attr_1000_b
)放入同一个元素中。
让我举个例子。输入XML后:
<root>
<row id="1">
<foo attr_1000_a="true">1</foo>
<foo attr_1000_b="true">2</foo>
<foo attr_1000_c="true">3</foo>
</row>
<row id="2">
<foo attr_1000_a="true" attr_1000_b="true" attr_1000_c="true">10</foo>
<foo attr_2000_a="true" attr_2000_b="true" attr_2000_c="true">20</foo>
</row>
<row id="3">
<foo attr_1000_a="true" attr_2000_a="true" attr_3000_a="true">100</foo>
<foo attr_1000_b="true" attr_2000_b="true" attr_3000_b="true">200</foo>
<foo attr_1000_c="true" attr_2000_c="true" attr_3000_c="true">300</foo>
</row>
</root>
您可以看到属性可以通过多种方式组合,这使得转换变得困难。每个属性在每个<row>
中都是唯一的,但可以位于任何<foo>
元素中。此外,每个<foo>
可以具有任意数量的属性。
期望的结果:
<result>
<row id="1">
<field attr="1000">
<a>1</a>
<b>2</b>
<c>3</c>
</field>
</row>
<row id="2">
<field attr="1000">
<a>10</a>
<b>10</b>
<c>10</c>
</field>
<field attr="2000">
<a>20</a>
<b>20</b>
<c>20</c>
</field>
</row>
<row id="3">
<field attr="1000">
<a>100</a>
<b>200</b>
<c>300</c>
</field>
<field attr="2000">
<a>100</a>
<b>200</b>
<c>300</c>
</field>
<field attr="3000">
<a>100</a>
<b>200</b>
<c>300</c>
</field>
</row>
</result>
我想我必须以某种方式获得连续所有数字的列表(例如1000,2000和3000),然后遍历所有具有这种属性的元素。
我如何使用XSLT执行此操作?这甚至可能吗?
答案 0 :(得分:3)
以下是样式表示例:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:key name="k1"
match="row/foo/@*"
use="concat(generate-id(../..), '|', substring-before(substring-after(local-name(), '_'), '_'))"/>
<xsl:template match="root">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="row">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="foo/@*[generate-id() = generate-id(key('k1', concat(generate-id(../..), '|', substring-before(substring-after(local-name(), '_'), '_')))[1])]" mode="field"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo/@*" mode="field">
<field attr="{substring-before(substring-after(local-name(), '_'), '_')}">
<xsl:apply-templates select="key('k1', concat(generate-id(../..), '|', substring-before(substring-after(local-name(), '_'), '_')))"/>
</field>
</xsl:template>
<xsl:template match="foo/@*">
<xsl:element name="{substring-after(substring-after(local-name(), '_'), '_')}">
<xsl:value-of select=".."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:3)
快速而肮脏,这个xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="root">
<result>
<xsl:apply-templates select="@*|node()"/>
</result>
</xsl:template>
<xsl:template match="foo/@*">
<xsl:element name="{substring-after(local-name(),'000_')}">
<xsl:value-of select=".."/>
</xsl:element>
</xsl:template>
<xsl:template match="row">
<row id="{@id}">
<xsl:for-each-group select="foo/@*" group-by="substring(local-name(),1,9)">
<field attr="{substring-after(current-grouping-key(),'attr_')}">
<xsl:apply-templates select="current-group()"/>
</field>
</xsl:for-each-group>
</row>
</xsl:template>
<xsl:template match="foo">
<xsl:apply-templates select="@*"/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
已应用于此输入
<?xml version="1.0" encoding="UTF-8"?>
<root>
<row id="1">
<foo attr_1000_a="true">1</foo>
<foo attr_1000_b="true">2</foo>
<foo attr_1000_c="true">3</foo>
</row>
<row id="2">
<foo attr_1000_a="true" attr_1000_b="true" attr_1000_c="true">10</foo>
<foo attr_2000_a="true" attr_2000_b="true" attr_2000_c="true">20</foo>
</row>
<row id="3">
<foo attr_1000_a="true" attr_2000_a="true" attr_3000_a="true">100</foo>
<foo attr_1000_b="true" attr_2000_b="true" attr_3000_b="true">200</foo>
<foo attr_1000_c="true" attr_2000_c="true" attr_3000_c="true">300</foo>
</row>
</root>
产生此结果
<?xml version="1.0" encoding="UTF-8"?>
<result>
<row id="1">
<field attr="1000">
<a>1</a>
<b>2</b>
<c>3</c>
</field>
</row>
<row id="2">
<field attr="1000">
<a>10</a>
<b>10</b>
<c>10</c>
</field>
<field attr="2000">
<a>20</a>
<b>20</b>
<c>20</c>
</field>
</row>
<row id="3">
<field attr="1000">
<a>100</a>
<b>200</b>
<c>300</c>
</field>
<field attr="2000">
<a>100</a>
<b>200</b>
<c>300</c>
</field>
<field attr="3000">
<a>100</a>
<b>200</b>
<c>300</c>
</field>
</row>
</result>
魔法在
<xsl:element name="{substring-after(local-name(),'000_')}">
<xsl:value-of select=".."/>
</xsl:element>
这将创建具有动态名称的a / b / c元素,并向上移动一个节点以从父节点获取值(我们当前位于该属性中)。
和
<xsl:for-each-group select="foo/@*" group-by="substring(local-name(),1,9)">
<field attr="{substring-after(current-grouping-key(),'attr_')}">
<xsl:apply-templates select="current-group()"/>
</field>
</xsl:for-each-group>
使用他们的名称(foo/@*
)的一部分重新组合所有属性(substring(local-name(),1,9)
)。第一个随后以current-group()
形式提供,后者以current-grouping-key()
形式提供,如您所见。