6

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..

3
  • From where are you getting urn:sdcsdhc/shvhjcbenter code here inside value tags in your output transform? Commented Jan 24, 2013 at 20:14
  • What xslt processor are you using? Can you use xslt-2.0? And where did you get the names ExternalPublicOrgNWPlatformID_DDC and ExternalPublicOrgNWPlatformID_DS, which don't appear in the source XML? Commented Jan 24, 2013 at 21:45
  • Possible duplicate of XSLT parse text node as XML? Commented Dec 10, 2015 at 13:11

3 Answers 3

4

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>
Sign up to request clarification or add additional context in comments.

1 Comment

Note that this might handle the XML in the example, but it doesn't handle the whole of XML.
3

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.

1 Comment

I made an XML parser with xslt, see my answer below :)
1
The below is the transform for the expected output:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>
 <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>


  <xsl:template match="message">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>


      <xsl:apply-templates select="node()" />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="value">


    <xsl:apply-templates select="text()" />
  </xsl:template>
  <xsl:template match="text()[contains(., '&lt;OrgNetworkPlatformID>')]">
    <value>
      <xsl:value-of select="substring-before(substring-after(., '&lt;OrgNetworkPlatformID>'),
              '&lt;/OrgNetworkPlatformID>')"/>
    </value>
  </xsl:template>

</xsl:stylesheet>


The output xml :


<?xml version="1.0" encoding="utf-8"?>
<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>urn:vcloud:network:b7ccfd5f-cfd7-48eb-9dd6-1989b08d7b86</value>
    </agent-parameter>

  </send-parameters>
</message>

Is the above output XML are you looking for or something different?

4 Comments

Hi, I just given the sample xml inside the CDATA, I will have large xml inside the CDATA with multiple occurrences like <Networks><Network></Network><Network></Network><Network></Network></Networks> and I need to derive the output xml based on the conditions. Is it possible to write the XSLT to apply the logic on the XML inside the CDATA??
to the best of my knowledge we cannot..Which XSLT processor do you use?
Thanks for the quick response, It is XSLT 1.0 parser. Could we do this with any other processor?
@Shankar, This can be done easily in XSLT 3.0 -- would you be interested in an XSLT 3.0 solution?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.