0

I am trying to generate an XML output from the given input XML using XSLT. The output xml will have different namespaces added to it at various level of the xml tree. I am using XPath axes (descendant) to add the namespace to children nodes, but it outputs the grandchildern nodes next to children node.

My Input XML

<?xml version="1.0" encoding="UTF-8"?>
<request>
   <header>
      <sender>
         <User>ServiceUser</User>
         <userid type="UserId">ServiceUserid</userid>
      </sender>
      <receiver>
         <Country>IND</Country>
      </receiver>     
      <id>123456</id>
   </header>
   <Address type="physical">
      <subaddress>
         <number>2</number>
      </subaddress>
      <doornumber>13</doornumber>
      <streetName>mgr road</streetName>
      <locality>cityname</locality>
      <postalcode>612453</postalcode>
   </Address>
</request>

My XSLT

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aa="xyz.com/aa"
    xmlns:bb="xyz.com/bb" xmlns:addr="xyz.com/addr"  
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
     version="1.0">

       <xsl:output encoding="utf-8" indent="yes" method="xml" omit-xml-declaration="yes" />  
       <xsl:template match="/">
          <soapenv:Envelope>
             <soapenv:Header />
             <soapenv:Body>
                <xsl:copy>
                   <xsl:apply-templates select="node()|@*" />
                </xsl:copy>
             </soapenv:Body>
          </soapenv:Envelope>
       </xsl:template>
       <!-- template to copy attributes -->
       <xsl:template match="@*">
          <xsl:attribute name="{local-name()}">
             <xsl:value-of select="." />
          </xsl:attribute>
       </xsl:template>
       <!-- apply the 'event' namespace to the top node and its descendants -->
       <xsl:template match="//*">
          <xsl:choose>
             <!--Add namespace for the root element-->
             <xsl:when test="local-name() = 'request'">
                <xsl:element name="aa:{local-name()}">
                   <xsl:namespace name="aa" select="'xyz.com/aa'" />
                   <xsl:copy-of select="namespace::*" />
                   <xsl:apply-templates select="node()|@*" />
                </xsl:element>
             </xsl:when>
             <xsl:otherwise>
                <xsl:element name="{local-name()}">
                   <xsl:copy-of select="*" />
                </xsl:element>
             </xsl:otherwise>
          </xsl:choose>
       </xsl:template>
       <!-- template to copy the rest of the nodes -->
       <xsl:template match="*[ancestor-or-self::header]">
          <xsl:element name="bb:{name()}">
             <xsl:apply-templates select="node()|@*" />
          </xsl:element>
       </xsl:template>
       <xsl:template match="//Address">
          <xsl:element name="aa:{local-name()}">
             <xsl:for-each select="//Address/descendant::*">
                <xsl:element name="addr:{name()}">
                   <xsl:apply-templates select="node()|@*" />
                </xsl:element>
             </xsl:for-each>
          </xsl:element>
       </xsl:template>
       <xsl:template match="comment() | processing-instruction()">
          <xsl:copy />
       </xsl:template>
    </xsl:stylesheet>

Required Output XML

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:aa="xyz.com/request" xmlns:bb="xyz.com/header"
xmlns:addr="xyz.com/address">
    <soapenv:Header/>
    <soapenv:Body>
        <aa:request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <bb:header>
                <bb:sender>
                    <bb:User>ServiceUser</bb:User>
                    <bb:userid type="UserId">ServiceUserid</bb:userid>
                </bb:sender>
                <bb:receiver>
                    <bb:Country>IND</bb:Country>
                </bb:receiver>               
                <bb:id>123456</bb:id>
            </bb:header>
            <aa:Address type="physical">
                <addr:subaddress>
                    <number>2</number>
                </addr:subaddress>
                <addr:doornumber>13</addr:doornumber>
                <addr:streetName>mgr road</addr:streetName>
                <addr:locality>cityname</addr:locality>
                <addr:postalcode>612453</addr:postalcode>
            </aa:Address>
        </aa:request>
    </soapenv:Body>
</soapenv:Envelope>

Incorrect Ouput XML:

 <soapenv:Envelope xmlns:aa="xyz.com/aa"  
  xmlns:bb="xyz.com/bb"  xmlns:addr="xyz.com/addr"  
 xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
       <soapenv:Header/>
       <soapenv:Body>
          <aa:request>
             <bb:header>
               <!-- Correct output here -->
             </bb:header>
             <aa:Address>
                <addr:subaddress>
                   <number/> <!-- this element is empty -should have value "2" -->
                </addr:subaddress>
                <addr:number>2</addr:number> <!-- new element added which is incorrect-->
                <addr:doornumber>13</addr:doornumber>
                <addr:streetName>mgr road</addr:streetName>
                <addr:locality>cityname</addr:locality>
                <addr:postalcode>612453</addr:postalcode>
             </aa:Address>
          </aa:request>
       </soapenv:Body>
    </soapenv:Envelope>

Can you someone please help me with the correct xslt to produce the expected result. Thank you in advance.

3
  • Your stylesheet is tagged as version="1.0" but you use xsl:namespace which is only available in XSLT 2.0. Are you in fact using an XSLT 2.0 processor? Commented Feb 15, 2017 at 8:19
  • Also, shouldn't <number> be <addr:number> ? Commented Feb 15, 2017 at 9:17
  • @michael, it should be <number> without namespace. This xslt will be processed by a third party tool which supports both XSLT 1.0 and 2.0. Commented Feb 15, 2017 at 12:37

1 Answer 1

1

Why can't you do simply:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:bb="xyz.com/header"
xmlns:aa="xyz.com/request"
xmlns:addr="xyz.com/address">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="/request">
    <soapenv:Envelope >
        <soapenv:Header/>
        <soapenv:Body>
            <aa:request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <xsl:apply-templates/>
            </aa:request>
        </soapenv:Body>
    </soapenv:Envelope>
</xsl:template>

<xsl:template match="*[ancestor-or-self::header]">
    <xsl:element name="bb:{local-name()}">
        <xsl:apply-templates select="node()|@*" />
    </xsl:element>
</xsl:template>

<xsl:template match="Address">
    <aa:Address>
        <xsl:apply-templates select="node()|@*" />
    </aa:Address>
</xsl:template>

<xsl:template match="*[parent::Address]">
    <xsl:element name="addr:{local-name()}">
        <xsl:apply-templates select="node()|@*" />
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Note: the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" declaration is not being used and therefore entirely redundant.

Sign up to request clarification or add additional context in comments.

1 Comment

thanks for your reply. I am new to XSLT and still learning it. Will try your XSLT.

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.