在XSL

2011-04-20 42 views
1

識別獨特的子樹,我有一些XML,看起來像這樣:在XSL

<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」情況,以及對於這種情況再次一次:

<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

那麼,這是什麼問題?如果您要求某人提供「更加優雅」的解決方案,那麼您必須定義「優雅」。 – 2011-04-20 12:55:17

+0

@Dimitre,我上面提供的密鑰實際上並不能解決問題。這是一個討厭的黑客。我會澄清。 – 2011-04-20 12:56:16

+0

此外,你必須顯示必須產生的確切輸出 - 否則它沒有被定義,這顯然不是一個問題...... – 2011-04-20 12:58:48

回答

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中生成有效的名稱,也可以使用無限嵌套結構(我可以在另一個答案中做這個工作 - 但是,我們需要對這個更一般的問題更精確的定義)。

+0

Dimitre,非常感謝。我仍在努力將這個答案集成到我自己的XSLT中。只要我這樣做,並證實自己它的工作,我會標記它是正確的。 – 2011-04-20 17:20:13

+0

@ Jon-Bright:不客氣。 – 2011-04-20 17:27:16

+0

+1正確答案:複雜鍵(超出XPath/XSLT表達式的範圍)將需要此轉換組合。 – 2011-04-20 22:49:30