Выявление уникальных поддеревьев в XSL - PullRequest
1 голос
/ 20 апреля 2011

У меня есть некоторый 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, который я использую для генерации кода Java из этого XML.Ранее я делал ключ, а затем генерировал класс 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" и еще раз один раз для этого случая:

  <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 Ответ

1 голос
/ 20 апреля 2011

Это преобразование соответствует требованиям:

<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, а также для выполнения этой работы со структурами неограниченной вложенности (что я могу сделать в другом ответе - однако нам нужен более точное определение для этого более общего провлем).

...