使用 xslt 将 xml 结构转换为另一个 xml 结构
我有一个问题.我有以下源 xml 文件:
I have a question. I have the following source xml file:
源 xml:
<Container>
<DataHeader>
<c id="b" value="TAG" />
<c id="g" value="Info" />
</DataHeader>
<Data>
<Rows>
<r no="1">
<c id="b" value="uid1" uid="T.A.uid1" />
<c id="g" value="uid1|tag1|attr1|somevalue1" />
</r>
<r no="1">
<c id="b" value="uid1" uid="T.A.uid1" />
<c id="g" value="uid1|tag1|attr2|somevalue2" />
</r>
<r no="2">
<c id="b" value="uid1" uid="T.A.uid1" />
<c id="g" value="uid1|tag2|attr3|somevalue3" />
</r>
<r no="10">
<c id="b" value="uid2" uid="T.A.uid2" />
<c id="g" value="uid2|tag1|attr1|somevalue4" />
</r>
<r no="11">
<c id="b" value="uid2" uid="T.A.uid2" />
<c id="g" value="uid2|tag2|attr3|somevalue5" />
</r>
</Rows>
</Data>
</Container>
id 为 'g' 的元素 'c' 在源 xml 中很重要.这是一个连接字符串,其值由|"分隔.我们需要这些值来制作目标 xml.您可以使用 ID 为b"的元素c"来分隔uid".
The element 'c' with id 'g' is important in the source xml. This is a concatened string which values are seperated by a '|'. We need this values to make the target xml. The element 'c' with id 'b' you can use to separate the 'uid'.
值的示例和解释:
<c id="g" value="uid1|tag1|attr1|somevalue1" />
**uid value** | element node | **attribute** | attribute value
**uid1** | tag1 | **attr1** |somevalue1
具有相同uid"的所有元素必须聚合为 1 个单独的TestTag"元素(请参阅目标 xml).需要将具有相同父元素(例如tag1")的所有属性(attr1、attr2)添加到 1 个元素中.我只能使用 xslt(xpath) 1.0.
Al elements with the same 'uid' have to be aggregated into 1 single "TestTag" element (see target xml). Al attributes (attr1, attr2) with same parent element (for example 'tag1') needs to be added to 1 element. I only can make use of xslt(xpath) 1.0.
转换后的目标xml文件应该是这样的.
The target xml file should look like this after transforming.
xsl转换后的目标xml:
<Container>
<TestTag>
<object UID="T.A.uid1" Name="uid1"/>
<tag1 attr1="somevalue1" attr2="somevalue2"/>
<tag2 attr3="*somevalue3"/>
</TestTag>
<TestTag>
<Iobject UID="T.A.uid2" Name="uid2"/>
<tag1 attr1="somevalue4" />
<tag2 attr3="somevalue5"/>
</TestTag>
</Container>
将源 xml 转换为目标 xml 有哪些可能的解决方案?我尝试了几件事,但我现在卡住了.
What are possible solutions for transforming source xml to target xml? I tried several things but I'm stuck right now.
这并不完全困难,但由于 substring- 的广泛(但必要)嵌套使用而令人难以置信before()
和 substring-after()
.
This is not exactly difficult, but is mind-boggling due to extensive (yet necessary) nested use of substring-before()
and substring-after()
.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- index <c> nodes by their @id + "uid value" -->
<xsl:key name="kObject" match="r/c" use="
concat(@id, '|', @value)
" />
<!-- index <c> nodes by their @id + "uid value" -->
<xsl:key name="kTagByUid" match="r/c" use="
concat(@id, '|', substring-before(@value, '|'))
" />
<!-- index <c> nodes by their @id + "uid value" + "tag name" -->
<xsl:key name="kTagByName" match="r/c" use="
concat(@id, '|',
substring-before(
@value,
substring-after(substring-after(@value, '|'), '|')
)
)
" />
<xsl:variable name="vTagId" select="/Container/DataHeader/c[@value='TAG'][1]/@id" />
<xsl:variable name="vInfoId" select="/Container/DataHeader/c[@value='Info'][1]/@id" />
<!-- processing starts here -->
<xsl:template match="Container">
<xsl:copy>
<!-- apply templates to unique <c @id=$vTagId> tags -->
<xsl:apply-templates mode="tag" select="
Data/Rows/r/c[@id=$vTagId][
generate-id()
=
generate-id(key('kObject', concat(@id, '|', @value))[1])
]
" />
</xsl:copy>
</xsl:template>
<xsl:template match="c" mode="tag">
<TestTag>
<object UID="{@uid}" name="{@value}" />
<!-- apply templates to unique <c @id="g"> tags -->
<xsl:apply-templates mode="info" select="
key('kTagByUid', concat($vInfoId, '|', @value))[
generate-id()
=
generate-id(
key(
'kTagByName',
concat(@id, '|',
substring-before(
@value,
substring-after(substring-after(@value, '|'), '|')
)
)
)[1]
)
]
" />
</TestTag>
</xsl:template>
<xsl:template match="c" mode="info">
<!-- select 'uid1|tag1|' - it's the key to kTagByName -->
<xsl:variable name="key" select="substring-before(@value, substring-after(substring-after(@value, '|'), '|'))" />
<!-- select 'tag1' - it's the element name -->
<xsl:variable name="name" select="substring-before(substring-after($key, '|'), '|')" />
<xsl:element name="{$name}">
<xsl:for-each select="key('kTagByName', concat(@id, '|', $key))">
<!-- select 'attr1|somevalue1' - it's the attribute definition -->
<xsl:variable name="attrDef" select="substring-after(@value, $key)" />
<!-- create an attribute -->
<xsl:attribute name="{substring-before($attrDef, '|')}">
<xsl:value-of select="substring-after($attrDef, '|')" />
</xsl:attribute>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
生成:
<Container>
<TestTag>
<object UID="T.A.uid1" name="uid1" />
<tag1 attr1="somevalue1" attr2="somevalue2"></tag1>
<tag2 attr3="somevalue3"></tag2>
</TestTag>
<TestTag>
<object UID="T.A.uid2" name="uid2" />
<tag1 attr1="somevalue4"></tag1>
<tag2 attr3="somevalue5"></tag2>
</TestTag>
</Container>
注意这里没有注意重复的属性定义.如果您碰巧有 uid1|tag1|attr1|somevalue1
和更高版本的 uid1|tag1|attr1|othervalue1
,那么您最终会得到一个属性:attr1="othervalue1"
因为在 <xsl:for-each>
中,两者都轮到他们,后者获胜(即最终在输出中).
Note that this does not pay attention to duplicate attribute definitions. If you happen to have uid1|tag1|attr1|somevalue1
and later uid1|tag1|attr1|othervalue1
, then you will end up with one attribute: attr1="othervalue1"
because in the <xsl:for-each>
both get their turn, and the latter one wins (i.e. ends up in the output).
也可以满足这一点,它需要一个更多的键和一个更多的 Muenchian 分组,我将把它留给读者作为练习.呵呵.;)
It is possible to cater for that as well, it would require one more key and one more Muenchian grouping, I'm going to leave that as an exercise for the reader. Heh. ;)