在XSL中识别唯一的子树

时间:2011-04-20 12:48:21

标签: xslt xpath xslt-1.0

我有一些看起来像这样的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>

...因为组内的字段与其他情况下的字段不同。我上面的关键不包括这种情况。有办法吗?

1 个答案:

答案 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中产生有效的名称,并使这个工作使用无限嵌套的结构(我可以在另一个答案中做 - 但是,我们需要一个这个更普遍的问题的更精确的定义。)