Xslt转换/如何分组实体

时间:2011-06-06 09:14:12

标签: 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的每个Customer实体分组到Office实体中。如果有多个CodeModifier,我会添加Office实体。将修改Office中的Code属性(将Client的CodeModifier连接到Office)
  • (我觉得兼容但很简单)压缩CodeModifier属性并保留所有其他属性

有人知道该怎么做吗?

3 个答案:

答案 0 :(得分: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 :(得分:1)

这是另一种方法,仅使用匹配模板。


MSXSL 4.0

下测试 XSLT 1.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>

答案 2 :(得分:1)

完整而简短的XSLT 2.0解决方案

<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文档):

<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>