2011-06-06 61 views
1

我想用XSLT来修改:XSLT转换/如何分组实体

<Office Code="1" OtherAttribute="5"> 
    <Customer CustomerId="0010" CodeModifier="A"/> 
    <Customer CustomerId="0011" CodeModifier="B"/> 
    <Customer CustomerId="0012" CodeModifier="B"/> 
</Office> 
<Office Code="2" OtherAttribute="6"> 
    <Customer CustomerId="2010" CodeModifier="A"/> 
    <Customer CustomerId="0011" CodeModifier="C"/> 
</Office> 

到:

<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010"/> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011"/> 
    <Customer CustomerId="0012"/> 
</Office> 
<Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010"/> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011"/> 
</Office> 

我的目标:

  • 组具有相同的每个客户实体CodeModifier转换成Office实体。如果有多个CodeModifier,我将添加Office实体。 CODE属性到Office将被修改(客户端CodeModifier的串联到Office)
  • (兼,但琐碎的,我认为)抑制CodeModifier属性,并保持所有其他属性

是否有人知道该怎么做?

+0

问得好,+1。查看我的答案,获取完整,非常简短的XSLT 2.0解决方案。 :) – 2011-06-06 13:36:54

回答

1

这里的另一种方法,只用匹配模板。


测试为XSLT 1.0MSXSL 4.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:key name="kCustomerGroup" match="Customer" 
     use="concat(../@Code, @CodeModifier)" 
     /> 

    <xsl:template match="Office"> 
     <xsl:apply-templates select="Customer[generate-id() 
      = 
      generate-id(key('kCustomerGroup', 
      concat(../@Code, @CodeModifier))[1])]" 
      /> 
    </xsl:template> 

    <xsl:template match="Customer"> 
     <Office 
      Code="{concat(../@Code,@CodeModifier)}" 
      OtherAttribute="{../@OtherAttribute}"> 

      <xsl:apply-templates select="key('kCustomerGroup', 
       concat(../@Code,@CodeModifier))" mode="copy"/> 

     </Office> 
    </xsl:template> 

    <xsl:template match="Customer" mode="copy"> 
     <xsl:copy> 
      <xsl:copy-of select="@CustomerId"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

输出到下:

<?xml version="1.0" encoding="UTF-8"?> 
<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010"/> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011"/> 
    <Customer CustomerId="0012"/> 
</Office> 
<Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010"/> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011"/> 
</Office> 
+0

你正在像我一样使用条件。只有你的条件是在一个谓词中,而我的是在''中。 ;-)如果你仔细观察,我没有使用循环来做任何工作。 – Tomalak 2011-06-06 11:30:35

+0

我明白你的意思了,我已经编辑了我的描述。 – 2011-06-06 11:40:24

2

这个样式表,适用于您的输入样本:

<!-- a key to group Customers by their office code + modifier --> 
<xsl:key name="kCustomerGroup" match="Customer" 
    use="concat(../@Code, @CodeModifier)" 
/> 

<!-- identity template: copies everything that is not handled otherwise --> 
<xsl:template match="node() | @*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node() | @*" /> 
    </xsl:copy> 
</xsl:template> 

<!-- apply templates directly to customers. modify as necessary --> 
<xsl:template match="/"> 
    <xsl:apply-templates select="//Customer" mode="CustomerGroup" /> 
</xsl:template> 

<xsl:template match="Customer" mode="CustomerGroup"> 
    <xsl:variable name="officeCode" select="concat(../@Code, @CodeModifier)" /> 
    <!-- if this Customer is first of his respective group... --> 
    <xsl:if test=" 
    generate-id() 
    = 
    generate-id(key('kCustomerGroup', $officeCode)[1]) 
    "> 
    <!-- use for-each to switch the context node to the parent --> 
    <xsl:for-each select=".."> 
     <xsl:copy> 
     <xsl:apply-templates select="@*" /> 
     <!-- overwrite the @Code attribute --> 
     <xsl:attribute name="Code"> 
      <xsl:value-of select="$officeCode" /> 
     </xsl:attribute> 
     <!-- now handle the Customer group members --> 
     <xsl:apply-templates select="key('kCustomerGroup', $officeCode)" /> 
     </xsl:copy> 
    </xsl:for-each> 
    </xsl:if> 
</xsl:template> 

<!-- remove unwanted attribute with empty template -->  
<xsl:template match="Customer/@CodeModifier" /> 

回报

<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010"></Customer> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011"></Customer> 
    <Customer CustomerId="0012"></Customer> 
</Office> 
<Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010"></Customer> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011"></Customer> 
</Office> 

  • 使用的模板模式。我专门为Customer分组制作了一个模板。正常节点处理发生在身份模板中。
  • 使用单节点xsl:for-each循环更改xsl:copy的上下文节点。
  • 使用空模板从输出中删除节点。
  • 你可以用xsl:copy复制属性,以后覆盖其中的一个。
  • 输出按文档顺序排列。

要强制特定的顺序,使用这样的:

<xsl:apply-templates select="//Customer" mode="CustomerGroup"> 
    <xsl:sort select="../@Code" data-type="text" order="ascending" /> 
</xsl:apply-templates> 

<xsl:apply-templates select="key('kCustomerGroup', $officeCode)"> 
    <xsl:sort select="@CodeModifier" data-type="number" order="ascending" /> 
</xsl:apply-templates> 
1

一个完整的和短XSLT 2。

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/*/*"> 
    <xsl:for-each-group select="Customer" group-by="@CodeModifier"> 
    <Office> 
    <xsl:copy-of select="../@*"/> 
    <xsl:attribute name="Code" select= 
     "concat(../@Code, current-grouping-key())"/> 
    <xsl:copy-of select="current-group()"/> 
    </Office> 
    </xsl:for-each-group> 
</xsl:template> 
</xsl:stylesheet> 

当在下面的XML文档施加(基于所提供的XML片段,只是包成一个单一的顶部元件,以使之成为一个良好的XML文档)::0溶液

<company> 
    <Office Code="1" OtherAttribute="5"> 
     <Customer CustomerId="0010" CodeModifier="A"/> 
     <Customer CustomerId="0011" CodeModifier="B"/> 
     <Customer CustomerId="0012" CodeModifier="B"/> 
    </Office> 
    <Office Code="2" OtherAttribute="6"> 
     <Customer CustomerId="2010" CodeModifier="A"/> 
     <Customer CustomerId="0011" CodeModifier="C"/> 
    </Office> 
</company> 

产生想要的,正确的结果

<Office Code="1A" OtherAttribute="5"> 
    <Customer CustomerId="0010" CodeModifier="A"/> 
</Office> 
<Office Code="1B" OtherAttribute="5"> 
    <Customer CustomerId="0011" CodeModifier="B"/> 
    <Customer CustomerId="0012" CodeModifier="B"/> 
</Office> 
    <Office Code="2A" OtherAttribute="6"> 
    <Customer CustomerId="2010" CodeModifier="A"/> 
</Office> 
<Office Code="2C" OtherAttribute="6"> 
    <Customer CustomerId="0011" CodeModifier="C"/> 
</Office>