我有一些看起来像这样的XML:
<root>
<message name="peter">
<field type="integer" name="pa" />
<group name="foo">
<field type="integer" name="action" />
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
<message name="wendy">
<field type="string" name="wa" />
<group name="foo">
<field type="integer" name="action" />
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
</root>
我有一些XSL用于从这个XML生成Java代码。以前我一直在制作密钥,然后为每个组生成一个Java类。
<xsl:key name="groupsByName" match="//group" use="@name"/>
....
<xsl:for-each select="//group[generate-id(.) = generate-id(key('groupsByName',@name)[1])]">
<xsl:call-template name="class-for-group"/>
</xsl:for-each>
一切都很好。现在,我发现一些消息的组使用与其他地方存在的组相同的名称,但缺少其中一个字段。要继续上面的示例XML:
<message name="nana">
<field type="string" name="na" />
<group name="foo">
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
存在名为“foo”的组,但它缺少名为“action”的字段。
我想要做的是为每个唯一的子树生成一个Java类。这可能吗?我无法弄清楚xsl:key
会是什么样子。我最接近的想法是
<xsl:key name="groupsKey" match="//group" use="concat(@name,count(*))"/>
适用于上述示例中的情况,但不是很优雅。如果有两个名为“foo”的组具有相同数量(但不同类型)的字段,则会失败,因此它实际上不是解决方案。
要明确的是,理想的密钥(或其他替代方案)最终只能为上面的“peter”和“wendy”案例调用一次模板,一次针对“nana”案例,对于这种情况再次调用一次:< / p>
<message name="hook">
<field type="string" name="ha" />
<group name="foo">
<field type="string" name="favourite_breakfast" />
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
...因为组内的字段与其他情况下的字段不同。我上面的关键不包括这种情况。有办法吗?
答案 0 :(得分:1)
此转换符合要求:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kGroupByType" match="group"
use="@type"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates />
</xsl:variable>
<xsl:apply-templates mode="pass2"
select="ext:node-set($vrtfPass1)/*"/>
</xsl:template>
<xsl:template match="group">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:call-template name="makeType"/>
</xsl:copy>
</xsl:template>
<xsl:template mode="pass2"
match="group[generate-id()
=
generate-id(key('kGroupByType',@type)[1])
]
">
class <xsl:value-of select="concat(@name, '|', @type)"/>
</xsl:template>
<xsl:template name="makeType">
<xsl:attribute name="type">
<xsl:text>(</xsl:text>
<xsl:for-each select="*">
<xsl:value-of select="@type"/>
<xsl:if test="not(position()=last())">+</xsl:if>
</xsl:for-each>
<xsl:text>)</xsl:text>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档(包含所有添加内容):
<root>
<message name="peter">
<field type="integer" name="pa" />
<group name="foo">
<field type="integer" name="action" />
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
<message name="wendy">
<field type="string" name="wa" />
<group name="foo">
<field type="integer" name="action" />
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
<message name="nana">
<field type="string" name="na" />
<group name="foo">
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
<message name="hook">
<field type="string" name="ha" />
<group name="foo">
<field type="string" name="favourite_breakfast" />
<field type="integer" name="id" />
<field type="integer" name="value" />
</group>
</message>
</root>
生成了想要的结果:
class foo|(integer+integer+integer)
class foo|(integer+integer)
class foo|(string+integer+integer)
作为一种练习,读者可以进一步调整它以在一个PL中产生有效的名称,并使这个工作使用无限嵌套的结构(我可以在另一个答案中做 - 但是,我们需要一个这个更普遍的问题的更精确的定义。)