Search code examples
sql-serverxmlcdata

CDATA with Child elements


I would like the outcome with a subset of the MakeType . Right now I get the entire block and not elements of the MakeType. When the XML resolves the Type, the Series and class tag an element for those should be created. This is on SQL Server Standard 2017.And I really don't know CDATA xml style and would not use it but the vendor requires the CDATA type.

       ---Create Temp Table         
            declare @RepCar table
            (
            [Name] varchar(10),
            [Make] varchar(10),
            [Model] varchar(10),
            [Price]  money,
            [Type] varchar(10),
            [Series]  varchar(10),
            [Class] Varchar(10)
            );
             insert into @RepCar
            (
            Name, Make, Model, Price, Type, Series, Class
            )
            values
            ('Car1', 'Make1', 'Model1', 100, 'Type1', 'IS', 'Sedan'),
            ('Car1', 'Make1', 'Model1', 100, 'Type1', 'LS' , 'Sport'),
            ('Car2', 'Make2', 'Model2', 200, 'Type2', 'M3' , 'Sport'),
            ('Car3', 'Make3', 'Model3', 300, 'Type3','GS350','Sedan');




           --Declare Variables   
            DECLARE @TransactionId NVARCHAR(100)
            DECLARE @TransactionDateTime DATETIME
            --Setting Variable
            SET @TransactionId= (SELECT CONVERT(VARCHAR, CURRENT_TRANSACTION_ID()))
            SET @TransactionDateTime= GETDATE()

          --Create the XML

            select 1 AS Tag,
            0 AS Parent,
            'CollectSamplingData' as 'Message!1!TransactionType!cdata',
            @TransactionId as 'Message!1!TransactionID!cdata',
            @TransactionDateTime  as 'Message!1!TransactionDate!cdata',
            [Name]  as 'Message!1!CName!cdata',
            [Make]  as 'Message!1!MakeCar!cdata',
            [Model]  as 'Message!1!MakeModel!cdata',
            [Price]   as 'Message!1!DataValue!cdata',
            [Type]  as 'Message!1!MakeType!cdata' ,

         -----This is the SQL that is'nt working.
            ( select 
             1 AS Tag,
             0 AS Parent,
            [Series]  as 'Message!2!MakeSeries!cdata',
             [Class]  as 'Message!2!MakeClass!cdata' 
               from @RepCar  
             FOR XML EXPLICIT 
            )
            from @RepCar 
            FOR XML EXPLICIT, ROOT('Message');

The Outcome should look like this.When the code does see a the MakeType should have the Series and class below as the child element. These are the desired output XML

     <Message>
        <Message>
        <TransactionType><![CDATA[CollectSamplingData]]></TransactionType>
        <TransactionID><![CDATA[1482282230]]></TransactionID>
        <TransactionDate><![CDATA[2020-02-03T11:05:17.340]]></TransactionDate>
        <CName><![CDATA[Car1]]></CName>
        <MakeCar><![CDATA[Make1]]></MakeCar>
        <MakeModel><![CDATA[Model1]]></MakeModel>
        <DataValue><![CDATA[100.0000]]></DataValue>
        <MakeType><![CDATA[Type1]]>
                           <Series><![CDATA[IS]></Series>
                           <Class><![CDATA[Sedan]]></Class>
                           <Series><![CDATA[LS]></Series>
                           <Class><![CDATA[Sport]]></Class>
                           <Series><![CDATA[M3]></Series>
                           <Class><![CDATA[Sport]]></Class>
                           <Series><![CDATA[GS350]></Series>
                           <Class><![CDATA[Sedan]]></Class>>


            </MakeType>
       </Message>  

Solution

  • I struggled to produce what you need by using FOR XML EXPLICIT. Eventually, I reverted to using XQuery FLWOR expression. Please remember that SQL Server XML data type cannot hold CDATA sections. You need to use the NVARCHAR(MAX) data type. Check it out here: How to use CDATA in SQL XML

    SQL

    -- DDL and sample data population, start
    DECLARE @RepCar TABLE
    (
        [Name] VARCHAR(10),
        [Make] VARCHAR(10),
        [Model] VARCHAR(10),
        [Price] MONEY,
        [Type] VARCHAR(10),
        [Series] VARCHAR(10),
        [Class] VARCHAR(10)
    );
    INSERT INTO @RepCar
    (
        Name,
        Make,
        Model,
        Price,
        Type,
        Series,
        Class
    )
    VALUES
    ('Car1', 'Make1', 'Model1', 100, 'Type1', 'IS', 'Sedan'),
    ('Car1', 'Make1', 'Model1', 100, 'Type1', 'LS', 'Sport'),
    ('Car2', 'Make2', 'Model2', 200, 'Type2', 'M3', 'Sport'),
    ('Car3', 'Make3', 'Model3', 300, 'Type3', 'GS350', 'Sedan');
    -- DDL and sample data population, end
    
    --Declare Variables   
    DECLARE @TransactionId NVARCHAR(100) = CURRENT_TRANSACTION_ID();
    DECLARE @TransactionDateTime DATETIME = GETDATE();
    
    DECLARE @lt NCHAR(4) = '&lt;'
        , @gt NCHAR(4) = '&gt;';
    
    SELECT REPLACE(REPLACE(TRY_CAST((SELECT 'CollectSamplingData' AS [TransactionType]
        , @TransactionId AS [TransactionID]
        , @TransactionDateTime AS [TransactionDate]
        , * 
    FROM @RepCar
    FOR XML PATH('r'), TYPE, ROOT('root')).query('<Messages><Message>
    {
    for $x in /root/r[1]
    return (<TransactionType>{concat("<![CDATA[", data($x/TransactionType[1]), "]]>")}</TransactionType>,
            <TransactionID>{concat("<![CDATA[", data($x/TransactionID[1]), "]]>")}</TransactionID>,
            <TransactionDate>{concat("<![CDATA[", data($x/TransactionDate[1]), "]]>")}</TransactionDate>,
            <CName>{concat("<![CDATA[", data($x/Name[1]), "]]>")}</CName>,
            <MakeCar>{concat("<![CDATA[", data($x/Make[1]), "]]>")}</MakeCar>,
            <MakeModel>{concat("<![CDATA[", data($x/Model[1]), "]]>")}</MakeModel>,
            <DataValue>{concat("<![CDATA[", data($x/Price[1]), "]]>")}</DataValue>,
            <MakeType>{concat("<![CDATA[", data($x/Type[1]), "]]>")}
            {
                for $y in /root/r
                return (
                    <Series>{concat("<![CDATA[", data($y/Series[1]), "]]>")}</Series>,
                    <Class>{concat("<![CDATA[", data($y/Class[1]), "]]>")}</Class>
                )
            }
        </MakeType>)
    }
    </Message></Messages>') AS NVARCHAR(MAX)), @lt,'<'), @gt, '>') AS [XML with CDATA sections];
    

    Output

    <Messages>
        <Message>
            <TransactionType><![CDATA[CollectSamplingData]]></TransactionType>
            <TransactionID><![CDATA[1149709]]></TransactionID>
            <TransactionDate><![CDATA[2020-02-03T16:23:43.020]]></TransactionDate>
            <CName><![CDATA[Car1]]></CName>
            <MakeCar><![CDATA[Make1]]></MakeCar>
            <MakeModel><![CDATA[Model1]]></MakeModel>
            <DataValue><![CDATA[100.0000]]></DataValue>
            <MakeType><![CDATA[Type1]]>
                <Series><![CDATA[IS]]></Series>
                <Class><![CDATA[Sedan]]></Class>
                <Series><![CDATA[LS]]></Series>
                <Class><![CDATA[Sport]]></Class>
                <Series><![CDATA[M3]]></Series>
                <Class><![CDATA[Sport]]></Class>
                <Series><![CDATA[GS350]]></Series>
                <Class><![CDATA[Sedan]]></Class>
            </MakeType>
        </Message>
    </Messages>