Home > Software design >  Select XML Node from multiple xPath based on ID using XSLT
Select XML Node from multiple xPath based on ID using XSLT

Time:11-27

I'm currently working on a task where I need to loop through two different sections of an XML file (two files are merged previously into this one) and search for an ID.

If the ID, color and quantaties values match in both places of the file, I need to select all the fields in the second file (fileB).

If not, then I need to select the fields from the first file (fileA).

Here is a sample of the XML:

<root>
  <fileA>
    <data>
        <id>123</id>
        <color>Green</color>
        <quantaties>5</quantaties>
    </data>
    <data>
        <id>456</id>
        <color>Red</color>
        <quantaties>7</quantaties>
    </data>
    <data>
        <id>789</id>
        <color>Blue</color>
        <quantaties>9</quantaties>
    </data>
  </fileA>
  <fileB>
    <data>
        <id>456</id>
        <color>Red</color>
        <quantaties>7</quantaties>
        <date>15-07-2021</date>
        <reason>Internal</reason>
    </data>
  </fileB>
</root>

In The Example above only id 456, with color red, and quantities 7, is present in both files. In this case I want to populate that one from fileB. So my desired output would be:

<root>
  <newFile>
    <data>
        <id>123</id>
        <color>Green</color>
        <quantaties>5</quantaties>
    </data>
    <data>
        <id>456</id>
        <color>Red</color>
        <quantaties>7</quantaties>
        <date>15-07-2021</date>
        <reason>Internal</reason>
    </data>
    <data>
        <id>789</id>
        <color>Blue</color>
        <quantaties>9</quantaties>
    </data>
  </newFile>
</root>

Remember that multiple fields must match, so not only the ID. Also the color and quantaties in order for fileB data to be picked. Can anyone help me out on this one? Been struggeling for a while.

CodePudding user response:

I would do it this way:

XSLT 3.0

<xsl:stylesheet version="3.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="fileB" match="fileB/data" use="id||color||quantaties"/>

<xsl:template match="/root">
    <xsl:copy>
        <newFile>
            <xsl:for-each select="fileA/data">
                <xsl:copy>
                    <xsl:copy-of select="*, key('fileB', id||color||quantaties)/(* except (id|color|quantaties))"/>
                </xsl:copy>
            </xsl:for-each>
        </newFile>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Note that this assumes color does not start or end with a number - otherwise you should insert separator characters in the key value.


Alternatively you could do:

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

<xsl:template match="/root">
    <xsl:copy>
        <newFile>
            <xsl:for-each-group select="*/data" group-by="id, color, quantaties" composite="yes">
                <xsl:copy>
                    <xsl:for-each-group select="current-group()/*" group-by="name()" >
                        <xsl:copy-of select="."/>
                    </xsl:for-each-group>
                </xsl:copy>
            </xsl:for-each-group>
        </newFile>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
  • Related