如何使用XSLT将不同和冗余数据合并到一个元素中?

时间:2011-01-19 18:45:56

标签: xml xslt

以下是输入:

<Parent>
 <child>
  <e1>ABC1</e1>
  <e2>XYX</e2>
  <e3>4382</e3>
  <e4>summary1</e4>
  <mName>PRICE</mName>
  <mValue>1234000</mValue>
 </child>
 <child>
  <e1>ABC1</e1>
  <e2>XYX</e2>
  <e3>4382</e3>
  <e4>summary1</e4>
  <mName>TYPE</mName>
  <mValue>SPORTS</mValue>
 </child>
 <child>
  <e1>ABC2</e1>
  <e2>QWE</e2>
  <e3>3456</e3>
  <e4>summary2</e4>
  <mName>TYPE</mName>
  <mValue>SEDAN</mValue>
 </child>
</Parent>

我想以这样一种方式合并子元素,它既有冗余又有不同的元素.Below是预期的输出,我不知道如何使用XSL实现这一点任何帮助。

预期产出:

<Parent>
 <child>
  <e1>ABC1</e1>
  <e2>XYX</e2>
  <e3>4382</e3>
  <e4>summary</e4>
  <mName>PRICE</mName>
  <mValue>1234000</mValue>
  <mName>TYPE</mName>
  <mValue>SPORTS</mValue>
 </child>
 <child>
  <e1>ABC2</e1>
  <e2>QWE</e2>
  <e3>3456</e3>
  <e4>summary2</e4>
  <mName>TYPE</mName>
  <mValue>SEDAN</mValue>
 </child>
</Parent>

从评论中更新

  

所有子元素都是唯一的   由'e1'元素标识。哪两个   子元素具有相同的e   元素,它们应该合并   有一个孩子的名单   多个mName和mValue元素

3 个答案:

答案 0 :(得分:1)

经过大量的研究和努力,我提出了更简单的方法,这里是xsl,它提供了所需的输出'

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

    <xsl:key name="Ids" match="Parent/child" use="e1"/>

    <xsl:template match="/Parent">
         <Parent>
            <xsl:for-each select="child[generate-id(.) 
                                        = generate-id(key('Ids', e1)[1])]">
                 <child>
                    <xsl:copy-of select="e1|e2|e3|e4"/>
                     <xsl:for-each select="key('Ids', e1)">
                            <xsl:copy-of select="mName|mValue"/>
                     </xsl:for-each>
                </child >
            </xsl:for-each>
        </Parent>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

这是Muenchian Grouping的工作。您将在StackOverflow上的XSLT标记中找到许多示例。

我相信你所说的是孩子元素由 e1,e2,e3 e4 元素唯一标识。如果两个元素具有相同的 e 元素,则应将它们合并,以便有一个,其中包含多个 mName的列表 mValue 元素。

首先,您需要定义一个键来帮助您对子元素进行分组

<xsl:key 
   name="children" 
   match="child" 
   use="concat(e1, concat('|', concat(e2, concat('|', concat(e3, concat('|', e4))))))"/>

这会创建一个键,该键使用可用于查找元素的子 e 元素的串联。请注意使用管道字符 | 来连接它们。如果您的任何元素可能包含竖线字符,则应将其更改为使用其他字符。

接下来,您需要匹配每个不同的第一个实例的所有匹配项。这是通过这个可怕的陈述

来完成的
<xsl:apply-templates 
   select="
     child[generate-id() 
       = generate-id(
         key('children', 
           concat(e1, concat('|', concat(e2, concat('|', concat(e3, concat('|', e4))))))
         )[1])]"/>

即匹配元素,这些元素恰好是我们键中该元素的第一个出现。

如果您匹配了不同的节点,则可以使用相同的 e 元素循环遍历所有其他节点

<xsl:for-each select="key('children', concat(e1, concat('|', concat(e2, concat('|', concat(e3, concat('|', e4)))))))">

完全放弃

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

   <xsl:key name="children" match="child" use="concat(e1, concat('|', concat(e2, concat('|', concat(e3, concat('|', e4))))))"/>

   <xsl:template match="/Parent">
      <xsl:copy>
         <xsl:apply-templates select="child[generate-id() = generate-id(key('children', concat(e1, concat('|', concat(e2, concat('|', concat(e3, concat('|', e4)))))))[1])]"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="child">
      <xsl:copy>
         <xsl:copy-of select="e1|e2|e3|e4" />
         <xsl:for-each select="key('children', concat(e1, concat('|', concat(e2, concat('|', concat(e3, concat('|', e4)))))))">
            <xsl:copy-of select="mName" />
            <xsl:copy-of select="mValue" />
         </xsl:for-each>
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

当您将其应用于输入XML时,它应该提供您想要的输出。

答案 2 :(得分:0)

试试这个:

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

  <xsl:key name="childindex" match="child" use="e1" />

  <xsl:template match="Parent">
    <xsl:copy>
      <xsl:apply-templates select="child[generate-id() = generate-id(key('childindex', e1)[1])]"/>

    </xsl:copy>
  </xsl:template>

  <xsl:template match="child">
    <xsl:variable name="all" select="key('childindex',e1)" />
    <xsl:copy>
      <xsl:apply-templates select="e1 | e2 | e3 | e4" />
      <xsl:apply-templates select="$all/mValue | $all/mName" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

有一个匹配子节点的模板,它找到具有相同索引的所有子元素,并输出所有这些元素的mValue和mNode元素。

第二个子模板将应用于具有相同索引的前一个兄弟元素的任何子元素,并且不输出任何内容,删除重复项。

编辑:修改了xslt,修改后的版本与Tim非常相似,只是填充子元素的方法不同。