SQL Server 2019 FOR XML nested nodes preserving CDATA

95 Views Asked by At

I have to build this payload

<?xml version="1.0" encoding="utf-8"?>
<shipment>
    <software>
        <application>MYRTL</application>
        <version>1.0</version>
    </software>
    <security>
        <customer>X00000</customer>
        <user>X00000</user>
        <password>password1</password>
        <langid>IT</langid>
    </security>
    <consignment action="I" cashondeliver="N" international="N" insurance="N">
        <labelType>T</labelType>
        <senderAccId>200200</senderAccId>
        <consignmenttype>T</consignmenttype>
        <actualweight>00008000</actualweight>
        <actualvolume>0000018</actualvolume>
        <totalpackages>2</totalpackages>
        <packagetype>C</packagetype>
        <division>D</division>
        <product>N</product>
        <insurancevalue>0000000000000</insurancevalue>
        <insurancecurrency>EUR</insurancecurrency>
        <reference><![CDATA[22X000223]]></reference>
        <collectiondate>20220818</collectiondate>
        <termsofpayment>S</termsofpayment>
        <systemcode>RL</systemcode>
        <systemversion>1.0</systemversion>
        <codfvalue>0000000000000</codfvalue>
        <codfcurrency>EUR</codfcurrency>
        <goodsdesc><![CDATA[Bread, Butter & Puré]]></goodsdesc>
        <addresses>
            <address>
                <addressType>S</addressType>
                <vatno>123456789123</vatno>
                <addrline1><![CDATA[Via Mondovì, n° 23]]></addrline1>
                <postcode><![CDATA[20125]]></postcode>
                <phone1><![CDATA[345]]></phone1>
                <phone2><![CDATA[3456345]]></phone2>
                <name><![CDATA[Jack & Joe srl]]></name>
                <country><![CDATA[IT]]></country>
                <town><![CDATA[Arquà Polesine]]></town>
                <province><![CDATA[RO]]></province>
                <email><![CDATA[mail@jack_and_joe.it]]></email>
            </address>
            <address>
                <addressType>C</addressType>
                <addrline1><![CDATA[12° Reggimento Granatieri, 14]]></addrline1>
                <postcode><![CDATA[00195]]></postcode>
                <phone1><![CDATA[321]]></phone1>
                <phone2><![CDATA[3214321]]></phone2>
                <name><![CDATA[Giosuè Rossë]]></name>
                <country><![CDATA[IT]]></country>
                <town><![CDATA[Gambolo']]></town>
                <province><![CDATA[TV]]></province>
                <email><![CDATA[[email protected]]]></email>
            </address>
            <address>
                <addressType>R</addressType>
                <addrline1><![CDATA[Hauptstraße 13]]></addrline1>
                <postcode><![CDATA[34100]]></postcode>
                <phone1><![CDATA[333]]></phone1>
                <phone2><![CDATA[333444555]]></phone2>
                <name><![CDATA[Noè Giassù]]></name>
                <country><![CDATA[IT]]></country>
                <town><![CDATA[Völs am Schlern]]></town>
                <province><![CDATA[BZ]]></province>
                <email><![CDATA[[email protected]]]></email>
            </address>
        </addresses>
        <collectiontrg>
            <priopntime>0900</priopntime>
            <priclotime>1200</priclotime>
            <secopntime>1400</secopntime>
            <secclotime>1800</secclotime>
            <availabilitytime>1600</availabilitytime>
            <pickupdate>18.08.2022</pickupdate>
            <pickuptime>1600</pickuptime>
            <pickupdays>1</pickupdays>
            <pickupinstr><![CDATA[Test Shipment ===> DO NOT COLLECT <===]]></pickupinstr>
        </collectiontrg>
        <dimensions itemaction="I">
            <itemsequenceno>1</itemsequenceno>
            <itemtype>C</itemtype>
            <itemreference><![CDATA[22X0002223_1]]></itemreference>
            <volume>0000009</volume>
            <weight>00003000</weight>
            <length>030000</length>
            <heigh>010000</heigh>
            <width>030000</width>
            <quantity>1</quantity>
        </dimensions>
        <dimensions itemaction="I">
            <itemsequenceno>2</itemsequenceno>
            <itemtype>C</itemtype>
            <itemreference><![CDATA[22X0002223_2]]></itemreference>
            <volume>0000009</volume>
            <weight>00005000</weight>
            <length>030000</length>
            <heigh>010000</heigh>
            <width>030000</width>
            <quantity>1</quantity>
        </dimensions>
    </consignment>
</shipment>

I had the bad idea to use T-SQL since all data are in SQL Server DB

I thought it was quite easy, and actually, it was, since was just required to nest some FOR XML PATH, TYPE subqueries.

Problems arose when considered that some fields could contain not standard charachters, therefore was better to use some CDATA fields.

I faced several problems since it appears that the only way to preserve CDATA is using FOR XML EXPLICIT that seems to be deprecated. However it was very difficult to find documentation.

Fortunately I found this post that helped me to make the reverse path:

Therefore I built a sproc with XML Explicit format:

SELECT  1 AS Tag,
        NULL AS Parent,

        'MYRTL' AS 'software!1!application!element',
        '1.0'   AS 'software!1!version!element',
       
        NULL AS 'security!2!customer!element',
        ...
        NULL AS 'security!2!langid!element',

        NULL AS 'consignment!3!action',
        ...
        NULL AS 'consignment!3!goodsdesc!CDATA',

        NULL AS 'addresses!4!address',

        NULL AS 'address!5!addressType!element',
        ...
        NULL AS 'address!5!town!CDATA',
       
        ...

        NULL  AS 'collectiontrg!9!priopntime!element',
        ...
        NULL  AS 'collectiontrg!9!pickupdate!element',



UNION ALL
SELECT  2 AS Tag,
        NULL AS Parent,
        ...
UNION ALL
SELECT  3 AS Tag,
        NULL AS Parent,
        ...
UNION ALL
SELECT  9 AS Tag,
        3 AS Parent,
        ...

FOR XML EXPLICIT, ROOT('shipment') 

It seems to be working well... although I think there has to be a better way to build it.

Now I have a further issue that I do not know how to solve, or better, I could solve it using a dynamic query, but I would avoid it:

New issue is that node shipment.consignment.addresses.address where addressType=='C'

has to be omitted if it contains the same values as shipment.consignment.addresses.address where addressType=='S'

furthermore the node shipment.consignment.collectiontrg has to appear only if the variable pickupDate is not null

Is there a way to avoid the dynamic query?

Is there a better way to build this query? Thanks

0

There are 0 best solutions below