Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to parse the xml inside CDATA of another xml using xslt?

I need to transform the XML inside the CDATA of the XML using the single XSLT.

I have an XML as below with xml inside the CDATA as in the below xml.

    <message channel-id="e01db0aa-b3db-4b6c-a055-7a0d5c1d1f20" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
    <send-parameters>
               <agent-parameter multi-valued="false">
                 <name>Networks</name>
                 <value><![CDATA[<Networks>
    <Network>
      <GroupCode>EXTPRI</GroupCode>
      <NetworkTypeId>LANI</NetworkTypeId>   
      <OrgNetworkPlatformName>EON-0cbu0cust12301dcd-D-PCL-0002</OrgNetworkPlatformName>
      <OrgNetworkPlatformID>urn:vcloud:network:b7ccfd5f-cfd7-48eb-9dd6-1989b08d7b86</OrgNetworkPlatformID>
      </Network>
      <Network>
      <GroupCode>EXTPRI</GroupCode>
      <NetworkTypeId>LANI</NetworkTypeId>   
      <OrgNetworkPlatformName>ABC-0cbu0cust12301dcd-D-PCL-XYZ</OrgNetworkPlatformName>
      <OrgNetworkPlatformID>urn:vcloud:network:b7ccfd5f-cfd7-48eb-9dd6-1989b08d7b86</OrgNetworkPlatformID>
    </Network>

    </Networks>]]></value>
               </agent-parameter>

                        </send-parameters>
          </message>

I need to transform the xml into :

    <?xml version="1.0" encoding="UTF-8"?>
    <message xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" channel-id="7652d759-4b32-44d4-8a27-9e390f0cae7b">
        <send-parameters>
            <agent-parameter multi-valued="false">
                <name>ExternalPublicOrgNWPlatformID_DDC</name>
                <value>EON-0cbu0cust12301dcd-D-PCL-0002</value>
            </agent-parameter>
                    <agent-parameter multi-valued="false">
                <name>ExternalPublicOrgNWPlatformID_DS</name>
                <value>ABC-0cbu0cust12301dcd-D-PCL-XYZ</value>
            </agent-parameter>
        </send-parameters>
    </message>

This is the sample output I have given there would be multiple nodes inside the xml that i need to traverse through and generate the output xml.

I am using the xslt by directing the xpath upto the node inside the cdata of the source xml. but it is giving empty as it was not in a tree structure format.

I can not get the X-Path for the xml inside the CDATA. It is working well if I remove CDATA in the xml , but the xml is comming from the external system which can not be modified.

I can't use multiple xslts I need to apply single XSLT.

Could you please suggest me on this.

Many thanks in anticipation..

like image 962
Shankar Avatar asked Jan 24 '13 18:01

Shankar


2 Answers

This XSLT produces the output required:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common" 
extension-element-prefixes="exsl">
<xsl:output indent="yes"/>
<xsl:template match="/">
    <xsl:variable name="cdata"
        select="message/send-parameters/agent-parameter/value"/>
    <xsl:variable name="parsedXml_">
        <xsl:call-template name="parseXml">
            <xsl:with-param name="text" select="$cdata"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="parsedXml" select="exsl:node-set($parsedXml_)"/>
    <message xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
        <xsl:attribute name="channel">
            <xsl:value-of select="message/@channel-id"/>
        </xsl:attribute>
        <send-parameters>
            <agent-parameter multi-valued="false">
                <name>ExternalPublicOrgNWPlatformID_DDC</name>
                <value>
                    <xsl:value-of select="$parsedXml/Networks/Network[1]/OrgNetworkPlatformName"/>
                </value>
            </agent-parameter>
            <agent-parameter multi-valued="false">
                <name>ExternalPublicOrgNWPlatformID_DS</name>
                <value>
                    <xsl:value-of select="$parsedXml/Networks/Network[2]/OrgNetworkPlatformName"/>
                </value>
            </agent-parameter>
        </send-parameters>
    </message>
</xsl:template>
<xsl:template name="parseXml">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="contains($text, '&gt;')">
            <xsl:variable name="topLevelTag">
                <xsl:call-template name="getTopLevelTag">
                    <xsl:with-param name="text" select="$text"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="openingTag">
                <xsl:value-of select="$topLevelTag"/>
            </xsl:variable>
            <xsl:variable name="tagName">
                <xsl:call-template name="getTopLevelTagName">
                    <xsl:with-param name="text" select="$text"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="closingTag">
                <xsl:value-of select="concat('&lt;/',$tagName,'&gt;')"/>
            </xsl:variable>
            <xsl:variable name="firstNode">
                <xsl:if test="not(contains($topLevelTag,'/&gt;'))">
                    <xsl:value-of select="substring-before(substring-after($text,$openingTag),$closingTag)"/>        
                </xsl:if>
            </xsl:variable>
            <xsl:variable name="afterFirstNode">
                <xsl:choose>
                    <xsl:when test="not(contains($topLevelTag,'/&gt;'))">
                        <xsl:value-of select="substring-after($text,concat($firstNode,$closingTag))"/>        
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="substring-after($text,$topLevelTag)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:element name="{$tagName}">
                <xsl:call-template name="createAttributes">
                    <xsl:with-param name="text" select="$topLevelTag"/>
                </xsl:call-template>
                <xsl:call-template name="parseXml">
                    <xsl:with-param name="text" select="$firstNode"/>
                </xsl:call-template>
            </xsl:element>
            <xsl:call-template name="parseXml">
                <xsl:with-param name="text" select="$afterFirstNode"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
<xsl:template name="getTopLevelTagName">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="contains($text, '&gt;')">
            <xsl:variable name="tagWithAttributesWithoutEnd">
                <xsl:value-of select="substring-before($text, '&gt;')"/>
            </xsl:variable>
            <xsl:variable name="tagWithAttributesWithoutBegining">
                <xsl:value-of select="substring-after($tagWithAttributesWithoutEnd, '&lt;')"/>
            </xsl:variable>
            <xsl:variable name="tagName">
                <xsl:choose>
                    <xsl:when test="contains($tagWithAttributesWithoutBegining,' ')">
                        <xsl:value-of
                            select="substring-before($tagWithAttributesWithoutBegining, ' ')"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$tagWithAttributesWithoutBegining"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:value-of select="$tagName"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>
<xsl:template name="getTopLevelTag">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="contains($text, '&gt;')">
            <xsl:variable name="tagWithAttributesWithoutEnd">
                <xsl:value-of select="substring-before($text, '&gt;')"/>
            </xsl:variable>
            <xsl:value-of select="concat($tagWithAttributesWithoutEnd,'&gt;')"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>
<xsl:template name="createAttributes">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="contains($text, '=&quot;')">
            <xsl:variable name="attributeName">
                <xsl:value-of select="substring-before(substring-after($text,' '),'=&quot;')"/>
            </xsl:variable>
            <xsl:message>
                <xsl:value-of select="$text"/>
            </xsl:message>
            <xsl:variable name="attributeValue">
                <xsl:value-of select="substring-before(substring-after($text,concat($attributeName,'=&quot;')),'&quot;')"/>
            </xsl:variable>
            <xsl:attribute name="{$attributeName}">
                <xsl:value-of select="$attributeValue"/>
            </xsl:attribute>
            <xsl:call-template name="createAttributes">
                <xsl:with-param name="text" select="substring-after($text,concat($attributeName,'=&quot;',$attributeValue,'&quot;'))"/>
            </xsl:call-template>
        </xsl:when>
    </xsl:choose>        
</xsl:template>
</xsl:stylesheet>
like image 62
jechaviz Avatar answered Nov 15 '22 10:11

jechaviz


CDATA means "character data", and it is properly used to indicate that the contained text does not contain any markup. If it is misused to wrap text that does contain markup, your only answer is to extract the textual content and put it through a second phase of parsing. XSLT 1.0 and 2.0 do not contain a function that allows you to parse lexical XML, but XSLT 3.0 does. If you're stuck with XSLT 1.0, you'll have to write your own extension function that passes the data to a parser and gets the root of the resulting node tree back.

like image 25
Michael Kay Avatar answered Nov 15 '22 09:11

Michael Kay