开发者

How to merge distinct and redundant data into one element using XSLT?

开发者 https://www.devze.com 2023-02-05 07:55 出处:网络
Following is the input : <Parent> <child> <e1>ABC1</e1> <e2>XYX</e2>

Following is the input :

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

I want to merge the child element in such a way that it has both redundant as well as the distinct elements.Below is the expected output,I am not sure how to achieve this using XSL any hel开发者_开发问答p appreciated.

Expected output :

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

Update from comments

All the child elements are uniquely identified by 'e1' element. Where two child elements have the same such e elements, they should be merged so there is one child with a list of multiple mName and mValue elements


After much of research and efforts i came up with simpler approach and here is the xsl which gives desired output '

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


This is a job for Muenchian Grouping. You will numerous examples of it within the XSLT tag here on StackOverflow.

I believe what you are saying is that child elements are uniquely identified by the elements e1, e2, e3 and e4. Where two child elements have the same such e elements, they should be merged so there is one child with a list of multiple mName and mValue elements.

First, you need to define a key to help you group the child elements

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

This creates a key that use the concatenation of the child e elements that can be used to look up child elements. Do note the use of the pipe character | to concatenate them. If any of your elements may contain a pipe character, then you should change this to use another character instead.

Next, you need to match all the occurrences of the first instance of each distinct child. This is done with this scary looking statement

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

i.e Match child elements which happen to be the first occurence of that element in our key.

When you have matched the distinct child nodes, you can then loop through all other child nodes with the same e elements

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

Putting this altogether gives

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

When you apply this to your input XML it should give the output you want.


Try this:

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

There's a template matching a child node that finds ALL child elements with the same indices, and will output the mValue and mNode elements of all those elements.

The second child template will be applied to any child elements that have a preceding sibling with the same indices, and outputs nothing, stripping out duplicates.

EDIT: Modified the xslt, and the modified version is very similar to Tim's, only the method of populating the child element differs.

0

精彩评论

暂无评论...
验证码 换一张
取 消