0

I would like to take some XML document and merge all nodes on some level who have the same attribute value. For instance, if I have a document of this form,

<xml>
 <books id="X">
  <book name="Story"/>
  <book name="Tale"/>
 </books>
 <books id="X">
  <book name="Folklore"/>
  <book name="Magic"/>
 </books>
 <books id="Y">
  <book name="Harry Potter"/>
  <book name="LotR"/>
 </books>
</xml>

then I would like to be able to convert it to the following document:

<xml>
 <books id="X">
  <book name="Story"/>
  <book name="Tale"/>
  <book name="Folklore"/>
  <book name="Magic"/>
 </books>
 <books id="Y">
  <book name="Harry Potter"/>
  <book name="LotR"/>
 </books>
</xml>

If at all possible, I would very much like to multiple solutions. For instance both one using XSLT and one using some other language.

misolsen
  • 3
  • 2
  • You cannot modify XML source with XPath. You should check XSLT instead. Also note that question that contain no attempts to the solve issue should be closed as *too broad* or *off-topic* – Andersson Sep 18 '18 at 07:32
  • This is a grouping issue. If you are using XSLT 1.0, then read up on [Muenchian Grouping](http://www.jenitennison.com/xslt/grouping/muenchian.html). If you can use XSLT 2.0 or above, then [xsl:for-each-group](https://www.xml.com/pub/a/2003/11/05/tr.html) will be your friend. If you can't get it to work, post what you have tried, and I am sure you will get an answer. Thanks! – Tim C Sep 18 '18 at 07:53

2 Answers2

0
<xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="books[1][@id='X']">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
        <xsl:apply-templates/>
            <xsl:apply-templates select="following-sibling::books[@id='X']/book"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="books[@id='X' and preceding-sibling::books[@id='X']]"/>
    <xsl:template match="book">
        <xsl:copy>
        <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
check it.
imran
  • 461
  • 4
  • 8
0

you can try this:

<xsl:template match="xml">
        <xsl:for-each-group select="books" group-by="@id">
            <xsl:copy>
                <xsl:attribute name="id" select="current-grouping-key()"/>
                <xsl:apply-templates select="current-group()/*"/>
            </xsl:copy>
        </xsl:for-each-group>
    </xsl:template>

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

See conversion at https://xsltfiddle.liberty-development.net/gWmuiJR

Rupesh_Kr
  • 3,395
  • 2
  • 17
  • 32