Search code examples
xsltubl

XSLT transformation: move tag values to parent ones


could you support me with the following XSLT transformation:

Initial xml file:

<root>
    <Invoice>
        <ID>
            <_>INV12345</_>
        </ID>
        <InvoiceTypeCode>
            <_>01</_>
            <listVersionID>1.0</listVersionID>
        </InvoiceTypeCode>
        <InvoicePeriod>
            <StartDate>
                <_>2017-11-26</_>
            </StartDate>
            <EndDate>
                <_>2017-11-30</_>
            </EndDate>
        </InvoicePeriod>
        <AccountingSupplierParty>
            <AdditionalAccountID>
                <_>id-1234</_>
                <schemeAgencyName>name1</schemeAgencyName>
            </AdditionalAccountID>
        </AccountingSupplierParty>
    </Invoice>
</root>

Final xml file:

<Invoice>
    <ID>NV12345</ID>
    <InvoiceTypeCode listVersionID="1.0">01</InvoiceTypeCode>
    <InvoicePeriod>
        <StartDate>2017-11-26</StartDate>
        <EndDate>2017-11-30</EndDate>
    </InvoicePeriod>
    <AccountingSupplierParty schemeAgencyName="name1">
        <AdditionalAccountID>id-1234</AdditionalAccountID>
    </AccountingSupplierParty>
</Invoice>

It's necessary to do:

  • remove root tag;
  • remove all tags <_>, but their values move to their parent tags;
  • all others non <_> tags move as attributes to their parent tags.

Such task is happened while Jackson transformation of UBL (Universal Business Language) from JSON to XML.

I've just found how to remove the root tag:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- identity template --> 
    <xsl:template match="node() | @*">   
        <xsl:copy>
            <xsl:apply-templates select="node() | @*" />
        </xsl:copy>
    </xsl:template>

    <!-- remove root tag --> 
    <xsl:template match="/*">   
        <xsl:apply-templates select="node()" /> 
    </xsl:template>   
</xsl:stylesheet>

Solution

  • I believe I am getting the expected result using this XSLT 1.0 stylesheet:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <!-- convert leaf child elements (except _) to attributes -->
            <xsl:for-each select="*[not(* or self::_)]">
                <xsl:attribute name="{name()}">
                    <xsl:value-of select="."/>
                </xsl:attribute>  
            </xsl:for-each>
            <!-- process other child elements and text nodes -->
            <xsl:apply-templates select="*[*] | _ | text() "/>
        </xsl:copy>
    </xsl:template>
    
    <!-- remove root and _ elements --> 
    <xsl:template match="/* | _">   
        <xsl:apply-templates/> 
    </xsl:template>
    
    </xsl:stylesheet>
    

    But this does not process comments and processing instructions.

    Bear in mind that removing the root element could cause the result to become an XML fragment, not a well-formed XML document.