简体   繁体   中英

Transformation from flat to nested XML in XSLT 1.0

I have the following flat xml structure

<?xml version="1.0" encoding="UTF-8"?>
<ns0:Main xmlns:ns0="urn:test">
    <RECORDSET>
        <Item>
            <Number>00011111</Number>
            <rowNumber>00001</rowNumber>
            <Product>Product 1</Product>
        </Item>
        <Item>
            <Number>00011111</Number>
            <rowNumber>00002</rowNumber>
            <Product>Product 2</Product>
        </Item>
        <Item>
            <Number>00099999</Number>
            <rowNumber>00001</rowNumber>
            <Product>Product 3</Product>
        </Item>
        <Tax>
            <Number>00011111</Number>
            <taxRowNumber>00001</taxRowNumber>
            <TaxType>TAX1</TaxType>
            <Amount>100</Amount>
        </Tax>
        <Tax>
            <Number>00011111</Number>
            <taxRowNumber>00001</taxRowNumber>
            <TaxType>TAX2</TaxType>
            <Amount>200</Amount>
        </Tax>
        <Tax>
            <Number>00099999</Number>
            <taxRowNumber>00001</taxRowNumber>
            <TaxType>TAX2</TaxType>
            <Amount>110</Amount>
        </Tax>
    </RECORDSET>
</ns0:Main>

And I need the following result. Each <Tax> element must be moved inside the proper <Item> (The one with the the same Number and rowNumber informations)

<?xml version="1.0" encoding="UTF-8"?>
<ns0:Main xmlns:ns0="urn:test">
    <RECORDSET>
        <Item>
            <Number>00011111</Number>
            <rowNumber>00001</rowNumber>
            <Product>Product 1</Product>
            <Tax>
                <Number>00011111</Number>
                <taxRowNumber>00001</taxRowNumber>
                <TaxType>TAX1</TaxType>
                <Amount>100</Amount>
            </Tax>
            <Tax>
                <Number>00011111</Number>
                <taxRowNumber>00001</taxRowNumber>
                <TaxType>TAX2</TaxType>
                <Amount>200</Amount>
            </Tax>
        </Item>
        <Item>
            <Number>00011111</Number>
            <rowNumber>00002</rowNumber>
            <Product>Product 2</Product>
        </Item>
        <Item>
            <Number>00099999</Number>
            <rowNumber>00001</rowNumber>
            <Product>Product 3</Product>
            <Tax>
                <Number>00099999</Number>
                <taxRowNumber>00001</taxRowNumber>
                <TaxType>TAX2</TaxType>
                <Amount>110</Amount>
            </Tax>
        </Item>
    </RECORDSET>
</ns0:Main>

I thought I could easily get what I need with the following XSLT ( XSLT version 1.0 is a must ), but the <Tax> element never appear.

I tried many changes but none is correct.

I also found a lot of "flat to nested XML" questions, but without luck

<?xml version="1.0"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="urn:test">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <ns0:Main>
            <RECORDSET>
                <xsl:for-each select="ns0:Main/RECORDSET/Item">
                    <Item>
                        <xsl:for-each select="Number">
                            <Number>
                                <xsl:value-of select="."/>
                            </Number>
                        </xsl:for-each>
                        <xsl:for-each select="rowNumber">
                            <rowNumber>
                                <xsl:value-of select="."/>
                            </rowNumber>
                        </xsl:for-each>
                        <xsl:for-each select="Product">
                            <Product>
                                <xsl:value-of select="."/>
                            </Product>
                        </xsl:for-each>
                        <xsl:variable name="KeyNumber" select="Number"/>
                        <xsl:variable name="KeyrowNumber" select="rowNumber"/>
                        <xsl:for-each select="ns0:Main/RECORDSET/Tax[ Number    = $KeyNumber and
                                                                      taxRowNumber = $KeyrowNumber ]">
                            <Tax>
                                <xsl:copy-of select="./*"/>
                            </Tax>
                        </xsl:for-each>
                    </Item>
                </xsl:for-each>
            </RECORDSET>
        </ns0:Main>
    </xsl:template>
</xsl:transform>

Thanks a lot for any help.

I would suggest you use a key to retrieve the matching Tax nodes. Try:

XSLT 1.0

<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="tax" match="Tax" use="concat(Number, '|', taxRowNumber)" />

<xsl:template match="/*">
    <xsl:copy>
        <RECORDSET>
            <xsl:for-each select="RECORDSET/Item">
                <xsl:copy>
                    <xsl:copy-of select="*"/>
                    <xsl:copy-of select="key('tax', concat(Number, '|', rowNumber))"/>
                </xsl:copy>
            </xsl:for-each>
        </RECORDSET>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM