Waqas Ali Razzaq
Waqas Ali Razzaq

Reputation: 669

Extract xml from repeating elements based on unique sub string in the value

I am trying to create xml structure that have repeating element based on input xml. In the input repeating element MDMTagTagProperties has only 2 fields Key and Value. Based on Key field we need to create a element in output xml and set the content of Value field to that. The last 3 numeric character of Key field are same for one subset. Below are the Input XML, xslt that I am trying and Desired Output.

Input:

<?xml version="1.0" encoding="UTF-8"?>
<ns1:Properties xmlns:ns1="http://www.test.org/cmm/xsd/mdmtag_01">
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.MeteringDirection.2.8.1</ns1:Key>
        <ns1:Value>TLV</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.MultiplicationFactor.1.8.2</ns1:Key>
        <ns1:Value>2.0</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.MeasureUnit.2.8.1</ns1:Key>
        <ns1:Value>KWH</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.RegisterType.1.8.2</ns1:Key>
        <ns1:Value>N</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.MeteringDirection.1.8.2</ns1:Key>
        <ns1:Value>LVR</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.MultiplicationFactor.2.8.1</ns1:Key>
        <ns1:Value>5.0</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.NrOfDigits.1.8.2</ns1:Key>
        <ns1:Value>5</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.MeasureUnit.1.8.2</ns1:Key>
        <ns1:Value>KWH</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.RegisterType.2.8.1</ns1:Key>
        <ns1:Value>P</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.NrOfDigits.2.8.1</ns1:Key>
        <ns1:Value>4</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.Id.2.8.1</ns1:Key>
        <ns1:Value>2.8.1</ns1:Value>
    </ns1:MDMTagTagProperties>
    <ns1:MDMTagTagProperties>
        <ns1:Key>register.Id.1.8.2</ns1:Key>
        <ns1:Value>1.8.2</ns1:Value>
    </ns1:MDMTagTagProperties>
</ns1:Properties>

XSLt:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://www.test.org/cmm/xsd/mdmtag_01" xmlns:ns5="http://www.test.org/cmm/xsd/outputmdmtag_01" exclude-result-prefixes="ns1" version="1.0">
    <xsl:output method="xml" indent="yes" />

   <xsl:template match="/ns1:Properties">
    <ns5:Output>
        <xsl:for-each select="ns1:MDMTagTagProperties[contains(ns1:Key,'register.Id')]">
            <ns5:Register>
                <ns5:RegisterId>
                     <xsl:value-of select="current()/ns1:Value[contains(ns1:Key,'register.Id')]" />
                </ns5:RegisterId>

                <ns5:RegisterType>
                 <xsl:value-of select="current()/ns1:Value[contains(ns1:Key,'register.RegisterType')]" />
                </ns5:RegisterType>
                <ns5:RegisterMultiplicationFactor>
                   <xsl:value-of select="current()/ns1:Value[contains(ns1:Key,'register.RegisterMultiplicationFactor')]" />
                </ns5:RegisterMultiplicationFactor>
                  <ns5:RegisterMeteringDirection>
                  <xsl:value-of select="current()/ns1:Value[contains(ns1:Key,'register.RegisterMeteringDirection')]" />
                </ns5:RegisterMeteringDirection>
                <ns5:RegisterMeasureUnit>
                    <xsl:value-of select="current()/ns1:Value[contains(ns1:Key,'register.RegisterMeasureUnit')]" />
                </ns5:RegisterMeasureUnit>
                 <ns5:RegisterNrOfDigits>
                   <xsl:value-of select="current()/ns1:Value[contains(ns1:Key,'register.RegisterNrOfDigits')]" />
                </ns5:RegisterNrOfDigits>
            </ns5:Register>
        </xsl:for-each>
    </ns5:Output>
</xsl:template>
</xsl:stylesheet>

CurrentOutput:

<?xml version="1.0" encoding="UTF-8"?>
<ns5:Output xmlns:ns5="http://www.test.org/cmm/xsd/outputmdmtag_01">
    <ns5:Register>
        <ns5:RegisterId />
        <ns5:RegisterType />
        <ns5:RegisterMultiplicationFactor />
        <ns5:RegisterMeteringDirection />
        <ns5:RegisterMeasureUnit />
        <ns5:RegisterNrOfDigits />
    </ns5:Register>
    <ns5:Register>
        <ns5:RegisterId />
        <ns5:RegisterType />
        <ns5:RegisterMultiplicationFactor />
        <ns5:RegisterMeteringDirection />
        <ns5:RegisterMeasureUnit />
        <ns5:RegisterNrOfDigits />
    </ns5:Register>
</ns5:Output>

ExpectedOutput:

<?xml version="1.0" encoding="UTF-8"?>
<ns5:Output xmlns:ns5="http://www.test.org/cmm/xsd/outputmdmtag_01">
    <ns5:Register>
        <ns5:RegisterId>1.8.2</ns5:RegisterId>
        <ns5:RegisterType>N</ns5:RegisterType>
        <ns5:RegisterMultiplicationFactor>2.0</ns5:RegisterMultiplicationFactor>
        <ns5:RegisterMeteringDirection>LVR</ns5:RegisterMeteringDirection>
        <ns5:RegisterMeasureUnit>KHW</ns5:RegisterMeasureUnit>
        <ns5:RegisterNrOfDigits>5</ns5:RegisterNrOfDigits>
    </ns5:Register>
    <ns5:Register>
        <ns5:RegisterId>2.8.1</ns5:RegisterId>
        <ns5:RegisterType>P</ns5:RegisterType>
        <ns5:RegisterMultiplicationFactor>5.0</ns5:RegisterMultiplicationFactor>
        <ns5:RegisterMeteringDirection>TLV</ns5:RegisterMeteringDirection>
        <ns5:RegisterMeasureUnit>KWH</ns5:RegisterMeasureUnit>
        <ns5:RegisterNrOfDigits>4</ns5:RegisterNrOfDigits>
    </ns5:Register>
</ns5:Output>

Any help would be a great favor.

Upvotes: 1

Views: 509

Answers (2)

Martin Honnen
Martin Honnen

Reputation: 167696

As an alternative, here is an XSLT 2.0 grouping approach:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"
    xpath-default-namespace="http://www.test.org/cmm/xsd/mdmtag_01"
    xmlns:ns5="http://www.test.org/cmm/xsd/outputmdmtag_01" version="2.0">

    <xsl:param name="result-order" as="xs:string*"
        select="'register.Id', 'register.RegisterType', 'register.MultiplicationFactor', 'register.MeteringDirection', 'register.MeasureUnit'"/>

    <xsl:output indent="yes"/>

    <xsl:template match="Properties">
        <ns5:Output>
            <xsl:for-each-group select="MDMTagTagProperties"
                group-by="substring(Key, string-length(Key) - 4)">
                <ns5:Register>
                    <xsl:apply-templates select="current-group()">
                        <xsl:sort
                            select="index-of($result-order, substring(Key, 1, string-length(Key) - 6))"
                        />
                    </xsl:apply-templates>
                </ns5:Register>
            </xsl:for-each-group>
        </ns5:Output>
    </xsl:template>

    <xsl:template match="MDMTagTagProperties">
        <xsl:element name="ns5:{replace(Key, '\.[0-9]', '')}">
            <xsl:value-of select="Value"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 1

Tim C
Tim C

Reputation: 70638

Consider using a key to look up the other values from the input XML

<xsl:key name="tags" match="ns1:MDMTagTagProperties" use="ns1:Key" />

Them, for example, to look up the MultiplicationFactor you would do this:

<xsl:value-of select="key('tags', concat('register.MultiplicationFactor.', ns1:Value))/ns1:Value" />

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://www.test.org/cmm/xsd/mdmtag_01" xmlns:ns5="http://www.test.org/cmm/xsd/outputmdmtag_01" exclude-result-prefixes="ns1" version="1.0">
    <xsl:output method="xml" indent="yes" />

   <xsl:key name="tags" match="ns1:MDMTagTagProperties" use="ns1:Key" />

   <xsl:template match="/ns1:Properties">
    <ns5:Output>
        <xsl:for-each select="ns1:MDMTagTagProperties[starts-with(ns1:Key,'register.Id')]">
            <ns5:Register>
                <ns5:RegisterId>
                     <xsl:value-of select="ns1:Value" />
                </ns5:RegisterId>
                <ns5:RegisterType>
                    <xsl:value-of select="key('tags', concat('register.RegisterType.', ns1:Value))/ns1:Value" />
                </ns5:RegisterType>
                <ns5:RegisterMultiplicationFactor>
                    <xsl:value-of select="key('tags', concat('register.MultiplicationFactor.', ns1:Value))/ns1:Value" />
                </ns5:RegisterMultiplicationFactor>
                <ns5:RegisterMeteringDirection>
                    <xsl:value-of select="key('tags', concat('register.MeteringDirection.', ns1:Value))/ns1:Value" />
                </ns5:RegisterMeteringDirection>
                <ns5:RegisterMeasureUnit>
                    <xsl:value-of select="key('tags', concat('register.MeasureUnit.', ns1:Value))/ns1:Value" />
                </ns5:RegisterMeasureUnit>
                 <ns5:RegisterNrOfDigits>
                    <xsl:value-of select="key('tags', concat('register.NrOfDigits.', ns1:Value))/ns1:Value" />
                </ns5:RegisterNrOfDigits>
            </ns5:Register>
        </xsl:for-each>
    </ns5:Output>
</xsl:template>
</xsl:stylesheet>

From looking at your expected output, you could actually make this more generic. Try this XSLT too:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://www.test.org/cmm/xsd/mdmtag_01" xmlns:ns5="http://www.test.org/cmm/xsd/outputmdmtag_01" exclude-result-prefixes="ns1" version="1.0">
    <xsl:output method="xml" indent="yes" />

   <xsl:key name="tags" match="ns1:MDMTagTagProperties" use="substring-after(substring-after(ns1:Key, '.'), '.')" />

   <xsl:template match="/ns1:Properties">
    <ns5:Output>
        <xsl:for-each select="ns1:MDMTagTagProperties[contains(ns1:Key,'register.Id')]">
            <ns5:Register>
                <xsl:for-each select="key('tags', ns1:Value)">
                    <xsl:element name="ns5:Register{substring-before(substring-after(ns1:Key, '.'), '.')}">
                        <xsl:value-of select="ns1:Value" />
                    </xsl:element>
                </xsl:for-each>
            </ns5:Register>
        </xsl:for-each>
    </ns5:Output>
</xsl:template>
</xsl:stylesheet>

This would only work if all the ns1:Key values are of the form "register.xxxxx.n.n.n" though.

Upvotes: 1

Related Questions