XSLT - 将节点合并到集合中

时间:2014-03-18 11:28:07

标签: xml xslt

我有以下源数据:

<DATA>
  <ITEM>
     <ID>28</ID>
     <BRAND>TGR</BRAND>
     <ACC_NO>143080</ACC_NO>
     <ACC_DESCR>account description</ACC_DESCR>
  </ITEM>
  <ITEM>
     <ID>28</ID>
     <BRAND>TGR</BRAND>
     <ACC_NO>272180</ACC_NO>
     <ACC_DESCR>account description</ACC_DESCR>
  </ITEM>
  <ITEM>
     <ID>32</ID>
     <BRAND>TGR</BRAND>
     <ACC_NO>159880</ACC_NO>
     <ACC_DESCR>account description</ACC_DESCR>
  </ITEM>
</DATA>

我想转变成:

<DATA>
  <ITEM>
     <ID>28</ID>
     <BRAND>TGR</BRAND>
     <ACCOUNTS>
       <ACCOUNT>
         <ACC_NO>143080</ACC_NO>
         <ACC_DESCR>account description</ACC_DESCR>
       <ACCOUNT>
       <ACCOUNT>
         <ACC_NO>272180</ACC_NO>
         <ACC_DESCR>account description</ACC_DESCR>
       <ACCOUNT>
     <ACCOUNTS>
  </ITEM>
  <ITEM>
     <ID>32</ID>
     <BRAND>TGR</BRAND>
     <ACCOUNTS>
       <ACCOUNT>
         <ACC_NO>159880</ACC_NO>
         <ACC_DESCR>account description</ACC_DESCR>
       <ACCOUNT>
     <ACCOUNTS>
  </ITEM>
</DATA>    

应用以下转换:

<xsl:template match="/">
   <xsl:for-each select="/DATA/ITEM">
      <ITEM>
        <ID>
          <xsl:value-of select="ID"/>
        </ID>
        <BRAND>
          <xsl:value-of select="BRAND"/>
        <BRAND>
        <ACCOUNTS>
          <ACCOUNT>
            <AccountNumber>
              <xsl:value-of select="ACC_NO"/>
            <AccountNumber>
            <AccountDescription>
              <xsl:value-of select="ACC_DESCR"/>
            <AccountDescription>
          </ACCOUNT>
        </ACCOUNTS>
      </ITEM>
    </xsl:for-each>
</xsl:template>

我将在ITEM中获得ACCOUNTS列表,但仍然没有将ITEM与相同的ID合并。基本上我总是会得到一个带有单个元素的集合,而不是所有相同ID的帐户。

有任何建议我该如何做并产生所需的输出?

由于

1 个答案:

答案 0 :(得分:1)

在XSLT 2.0中,您只需将for-each替换为for-each-group,然后添加内部for-each即可为每个组成员创建一个ACCOUNT

<xsl:template match="/">
   <xsl:for-each-group select="/DATA/ITEM" group-by="ID">
      <ITEM>
        <ID>
          <xsl:value-of select="ID"/>
        </ID>
        <BRAND>
          <xsl:value-of select="BRAND"/>
        <BRAND>
        <ACCOUNTS>
          <xsl:for-each select="current-group()">
            <ACCOUNT>
              <AccountNumber>
                <xsl:value-of select="ACC_NO"/>
              <AccountNumber>
              <AccountDescription>
                <xsl:value-of select="ACC_DESCR"/>
              <AccountDescription>
            </ACCOUNT>
          </xsl:for-each>
        </ACCOUNTS>
      </ITEM>
    </xsl:for-each-group>
</xsl:template>

在1.0中,最有效的方法是称为 Muenchian分组的技术 - 定义密钥给出分组标准,然后使用generate-id的技巧只提取给定ID的第一个实例作为整个组的代理。

<xsl:key name="itemById" match="ITEM" use="ID" />

<xsl:template match="/">
   <xsl:for-each select="/DATA/ITEM[
          generate-id() = generate-id(key('itemById', ID)[1])]">
      <ITEM>
        <ID>
          <xsl:value-of select="ID"/>
        </ID>
        <BRAND>
          <xsl:value-of select="BRAND"/>
        <BRAND>
        <ACCOUNTS>
          <xsl:for-each select="key('itemById', ID)">
            <ACCOUNT>
              <AccountNumber>
                <xsl:value-of select="ACC_NO"/>
              <AccountNumber>
              <AccountDescription>
                <xsl:value-of select="ACC_DESCR"/>
              <AccountDescription>
            </ACCOUNT>
          </xsl:for-each>
        </ACCOUNTS>
      </ITEM>
    </xsl:for-each>
</xsl:template>

key('itemById', ID)[1]为您提供第一个ITEM元素,其值与我们正在测试的当前值相同ID,然后比较两者的generate-id值允许您确定它们是否是同一个节点。

(就个人而言,我会使用模板而不是for-each来解决问题,但原则仍然相同)