2016-10-26 43 views
1

我正尝试将xml数据重新组织和分组。我能够得到它的工作,但我的代码必须包括一些东西,看起来像(至少对我来说)像一个解决方法。这里是我的示例文件:xslt gmuenchian与子组分组

data.xml中:

<data> 
    <record Group="g1" SubGroup="sg1">Record 1</record> 
    <record Group="g2" SubGroup="sg1">Record 2</record> 
    <record Group="g1" SubGroup="sg1">Record 3</record> 
    <record Group="g2" SubGroup="sg1">Record 4</record> 
    <record Group="g2" SubGroup="sg2">Record 5</record> 
    <record Group="g1" SubGroup="sg2">Record 6</record> 
</data> 

Stylesheet.xsl:

<?xml version="1.0" encoding="UTF-8"?> 

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 

    <xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/> 

    <xsl:key name="Group" match="record" use="@Group" /> 
    <xsl:key name="SubGroup" match="record" use="@SubGroup" /> 

    <xsl:template match="/data"> 
     <xsl:variable name="Records" select="record"/> 

     <data> 
      <xsl:for-each select="$Records[generate-id(.)=generate-id(key('Group',@Group)[1])]"> 
       <xsl:sort select="@Group"/> 
       <xsl:variable name="Group" select="@Group"/> 

       <xsl:call-template name="Group"> 
        <xsl:with-param name="Records" select="$Records[@Group = $Group]"/> 
        <xsl:with-param name="Group" select="$Group"/> 
       </xsl:call-template> 
      </xsl:for-each> 
     </data> 
    </xsl:template> 

    <xsl:template name="Group"> 
     <xsl:param name="Records"/> 
     <xsl:param name="Group"/> 

     <group name="{$Group}"> 
      <xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',@SubGroup)[1])]"> 
<!-- this works: <xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',@SubGroup)[@Group = $Group][1])]"> --> 
       <xsl:sort select="@SubGroup"/> 
       <xsl:variable name="SubGroup" select="@SubGroup"/> 

       <xsl:call-template name="SubGroup"> 
        <xsl:with-param name="Records" select="$Records[@SubGroup = $SubGroup]"/> 
        <xsl:with-param name="Group" select="$Group"/> 
        <xsl:with-param name="SubGroup" select="$SubGroup"/> 
       </xsl:call-template> 
      </xsl:for-each> 
     </group> 
    </xsl:template> 

    <xsl:template name="SubGroup"> 
     <xsl:param name="Records"/> 
     <xsl:param name="Group"/> 
     <xsl:param name="SubGroup"/> 

     <subgroup name="{$SubGroup}"> 
      <xsl:for-each select="$Records"> 
       <xsl:copy-of select="."/> 
      </xsl:for-each> 
     </subgroup> 
    </xsl:template> 

</xsl:stylesheet> 

这是生成的输出:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <group name="g1"> 
    <subgroup name="sg1"> 
     <record Group="g1" SubGroup="sg1">Record 1</record> 
     <record Group="g1" SubGroup="sg1">Record 3</record> 
    </subgroup> 
    </group> 
    <group name="g2"> 
    <subgroup name="sg2"> 
     <record Group="g2" SubGroup="sg2">Record 5</record> 
    </subgroup> 
    </group> 
</data> 

但这是输出,我想要:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <group name="g1"> 
    <subgroup name="sg1"> 
     <record Group="g1" SubGroup="sg1">Record 1</record> 
     <record Group="g1" SubGroup="sg1">Record 3</record> 
    </subgroup> 
    <subgroup name="sg2"> 
     <record Group="g1" SubGroup="sg2">Record 6</record> 
    </subgroup> 
    </group> 
    <group name="g2"> 
    <subgroup name="sg1"> 
     <record Group="g2" SubGroup="sg1">Record 2</record> 
     <record Group="g2" SubGroup="sg1">Record 4</record> 
    </subgroup> 
    <subgroup name="sg2"> 
     <record Group="g2" SubGroup="sg2">Record 5</record> 
    </subgroup> 
    </group> 
</data> 

问题是temp-name中的for-each循环名为“Group”。看起来,key()函数不适用于$ Records中包含的节点,而是整个输入XML文件。

我用xsltproc和saxon获得了相同的结果,所以我不认为这是我的xslt处理器中的一个bug。看来,我没有完全理解key()是如何工作的。

如果我向key()的输出添加一个额外的选择器[@Group = $Group],我会得到预期的结果。

有人可以解释发生了什么,为什么需要额外的选择器[@Group = $Group]

马里奥

+0

同样的问题在这里得到解决:http://stackoverflow.com/questions/39793149/xslt-template-doesnt-apply-on-direct-child-nodes/39793718 – uL1

回答

1

当你想要做的子分组,您需要同时使用主组和子组的串联关键

<xsl:key name="SubGroup" match="record" use="concat(@Group,'|', @SubGroup)" /> 

然后,就用它在同方式前,用串联

<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(@Group,'|', @SubGroup))[1])]"> 

试试这个XSLT(我也简化了记录您的通话命名模板时使用的键)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 

    <xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/> 

    <xsl:key name="Group" match="record" use="@Group" /> 
    <xsl:key name="SubGroup" match="record" use="concat(@Group,'|', @SubGroup)" /> 

    <xsl:template match="/data"> 
     <data> 
      <xsl:for-each select="record[generate-id(.)=generate-id(key('Group',@Group)[1])]"> 
       <xsl:sort select="@Group"/> 

       <xsl:call-template name="Group"> 
        <xsl:with-param name="Records" select="key('Group',@Group)"/> 
       </xsl:call-template> 
      </xsl:for-each> 
     </data> 
    </xsl:template> 

    <xsl:template name="Group"> 
     <xsl:param name="Records"/> 

     <group name="{@Group}"> 
      <xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(@Group,'|', @SubGroup))[1])]"> 
       <xsl:sort select="@SubGroup"/> 

       <xsl:call-template name="SubGroup"> 
        <xsl:with-param name="Records" select="key('SubGroup',concat(@Group,'|', @SubGroup))"/> 
       </xsl:call-template> 
      </xsl:for-each> 
     </group> 
    </xsl:template> 

    <xsl:template name="SubGroup"> 
     <xsl:param name="Records"/> 
     <subgroup name="{@SubGroup}"> 
      <xsl:for-each select="$Records"> 
       <xsl:copy-of select="."/> 
      </xsl:for-each> 
     </subgroup> 
    </xsl:template> 

</xsl:stylesheet> 
+0

非常感谢您的回答。您使用key和concat()和key()来选择参数非常酷。但我仍然不明白,为什么我必须在内部循环中考虑@Group,因为'$ Records'已经被减少为仅包含当前组的记录。 –

+0

@MarioKlebsch因为正如您已经正确猜测的那样,键(在XSLT 1.0中)可以在整个文档中工作。 –

+0

@ michael.hor257k正如你所说,我正确地总结了它,但直到现在,我还没有意识到它。非常感谢你。 –