1

I have a message

<soapenv:Envelope  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns="http://xmlns.oracle.com/policyautomation/hub/12.0/metadata/types">
    <soapenv:Header/>
    <soapenv:Body>          
    <load-request root="Vehicles" region="en-US" language="en-US" timezone="Etc/GMT">
        <tables>
            <table name="Vehicles">
                <link name="Cars" target="Car" />
            </table>
        </tables>
    </load-request>
    </soapenv:Body>

and I need to make two transformations to it:

  • remove SOAP Envelope
  • transform the contents of the body (load-request tag)

I do know how to morph load-request, and tried this solution to remove SOAP, but cannot manage to combine the two and remove the envelope AND transform the body (load-request) with single xslt. The result XML should be:

<load-request>
  <root>Vehicles</root>
  <region>en-US</region>
  <language>en-US</language>
  <timezone>Etc/GMT</timezone>
  <request-context>
    <parameter>
        <name>MyParam1</name>
        <value>MyValue</value>
    </parameter>
  </request-context>
  <tables>
    <table>
        <name>Vehicles</name>
        <link>
        <name>Cars</name>
        <target>Car</target>
        </link>
    </table>
  </tables>
 </load-request>

The XSLT I used:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<xsl:output method="xml" indent="yes"/> 
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template> 
<xsl:template match="soapenv:*">
    <xsl:apply-templates select="@* | node()" />        
</xsl:template>
<xsl:template match="load-request">
    <xsl:element name="load-request">
        <xsl:element name="root">
            <xsl:value-of select="@root"/>
        </xsl:element>
        <xsl:element name="region">
            <xsl:value-of select="@region"/>
        </xsl:element> 
        <xsl:element name="language">
            <xsl:value-of select="@language"/>
        </xsl:element> 
        <xsl:element name="timezone">
            <xsl:value-of select="@timezone"/>
        </xsl:element> 
        <xsl:apply-templates select="request-context"/> 
        <xsl:apply-templates select="tables"/> 
    </xsl:element>        
</xsl:template>
<xsl:template match="request-context">
    <xsl:element name="request-context">
        <xsl:for-each select="parameter">
            <xsl:element name="parameter">
                <xsl:element name="name">
                    <xsl:value-of select="@name"/>
                </xsl:element>
                <xsl:element name="value">
                    <xsl:value-of select="@value"/>
                </xsl:element>
            </xsl:element>
        </xsl:for-each>
    </xsl:element>        
</xsl:template> 
<xsl:template match="tables">
    <xsl:element name="tables">
        <xsl:for-each select="table">
            <xsl:element name="table">
                <xsl:element name="name">
                    <xsl:value-of select="@name"/>
                </xsl:element> 
                <xsl:apply-templates select="link"/> 
                <xsl:for-each select="field">
                    <xsl:element name="field">
                        <xsl:element name="name">
                            <xsl:value-of select="@name"/>
                        </xsl:element>                   
                    </xsl:element>
                </xsl:for-each>                     
            </xsl:element>
        </xsl:for-each>
    </xsl:element>        
</xsl:template> 
<xsl:template match="link">
    <xsl:element name="link"> 
        <xsl:element name="name">
            <xsl:value-of select="@name"/>
        </xsl:element>
        <xsl:element name="target">
            <xsl:value-of select="@target"/>
        </xsl:element>
    </xsl:element>    
</xsl:template>
<xsl:template match="field">
    <xsl:for-each select="field">
        <xsl:element name="field">
            <xsl:element name="name">
                <xsl:value-of select="@name"/>
            </xsl:element>                   
        </xsl:element>
    </xsl:for-each>        
 </xsl:template> 
</xsl:stylesheet>

Update: The answer works for the input. Could you please weigh in on additional tweak: In some of my scenarios turning attributes into elements is not enough. The message below

<soapenv:Envelope  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
                   xmlns="http://xmlns.oracle.com/policyautomation/hub/12.0/metadata/types">
<soapenv:Header/>
<soapenv:Body>  
    <load-request root="Complains">
        <field name="Explanation">
            <text-val name="Text">The client needs a new toothbrush</text-val>
        </field>
    </load-request>
</soapenv:Body>

needs to become

<load-request>
   <root>Complains</root>
   <field>
     <name>Explanation</name>
     <text-val>
       <name>Text</name>
       <value>The client needs a new toothbrush</value>
     </text-val>
   </field>
</load-request>
Community
  • 1
  • 1
user3010912
  • 391
  • 2
  • 6
  • 15
  • 1
    Why don't you post your attempt so we can fix it instead of starting from scratch. – michael.hor257k Oct 15 '14 at 01:06
  • I updated the question with XSLT for the unsuccessful attempt. This variation removes the envelope but leaves the body content untouched. – user3010912 Oct 15 '14 at 01:19
  • The main problem with your XSLT (other than being unduly complex) is that the templates do not match anything. This is because all the unprefixed nodes in the source XML are in the default namespace declared in the root `soapenv:Envelope` element. – michael.hor257k Oct 15 '14 at 01:37

1 Answers1

5

How about starting with something much simpler:

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/"
exclude-result-prefixes="soapenv">

<xsl:strip-space elements="*"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<!-- remove all elements in the soapenv namespace -->
<xsl:template match="soapenv:*">
    <xsl:apply-templates select="node()"/>
</xsl:template>

<!-- for the remaining elements (i.e. elements in the default namespace) ... -->
<xsl:template match="*">
    <!-- ... create a new element with similar name in no-namespace -->
    <xsl:element name="{local-name()}">
        <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

<!-- convert attributes to elements -->
<xsl:template match="@*">
    <xsl:element name="{local-name()}">
        <xsl:value-of select="." />
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Applied to your example input, fixed for well-formedness(!):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://xmlns.oracle.com/policyautomation/hub/12.0/metadata/types">
  <soapenv:Header/>
  <soapenv:Body>
    <load-request root="Vehicles" region="en-US" language="en-US" timezone="Etc/GMT">
      <tables>
        <table name="Vehicles">
          <link name="Cars" target="Car"/>
        </table>
      </tables>
    </load-request>
  </soapenv:Body>
</soapenv:Envelope>

produces the following result:

<?xml version="1.0" encoding="UTF-8"?>
<load-request>
   <root>Vehicles</root>
   <region>en-US</region>
   <language>en-US</language>
   <timezone>Etc/GMT</timezone>
   <tables>
      <table>
         <name>Vehicles</name>
         <link>
            <name>Cars</name>
            <target>Car</target>
         </link>
      </table>
   </tables>
</load-request>

Edit:

in response to your edit:

If any element with a text value can be transformed so that the text value turns into a child element named value, you could simply add another generic template to the stylesheet:

<xsl:template match="text()">
    <value>
        <xsl:value-of select="." />
    </value>
</xsl:template>

If the above is not true, and you need to address a specific element in the source XML explicitly, then you will need to declare the source's default namespace in your stylesheet, assign it a prefix and use that prefix when addressing the element. The stylesheet element, in such case, would look like this:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tps="http://xmlns.oracle.com/policyautomation/hub/12.0/metadata/types"
exclude-result-prefixes="soapenv tps">

and your template would be in the form of:

<xsl:template match="tps:text-val">
    <text-val>
        <!-- more instructions here -->
    </text-val>
</xsl:template>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Thanks, @michael.hor257k, it worked. Could you please explain which part is responsible for what? The reason I'm asking is that the actual XML-s may vary; not all of them will follow same pattern (element with multiple attributes transformed into a group with tags representing each attribute) so I won't get away with one template. There are going to be scenarios where the input element will have value, for example 'Honda Civic
    ' and the result would have to have a tag 'Honda Civic'
    – user3010912 Oct 15 '14 at 02:04
  • Sorry, have to re-enter:Thanks, @michael.hor257k, it worked. Could you please explain which part is responsible for what? The reason I'm asking is that the actual XML-s may vary; not all of them will follow same pattern (element with multiple attributes transformed into a group with tags representing each attribute) so I won't get away with one template. There are going to be scenarios where the element will also have value, for example `Honda Civic`, and it would have to turn `SedanHonda Civic` – user3010912 Oct 15 '14 at 02:13
  • 1
    @user3010912 Please don't post code in comments. I suspect that the stylesheet above will accommodate the input you describe just fine. If not, please edit your question and add the problematic input, along with the expected output. -- Meanwhile I will add some comments to the stylesheet in order to explain how it works. – michael.hor257k Oct 15 '14 at 02:20
  • thanks @michael.hor257k for helping! I added alternative input and desired output. Also - could you please elaborate on: "templates do not match anything. This is because all the unprefixed nodes in the source XML are in the default namespace declared in the root". What should be fixed in order for templates to find the match? – user3010912 Oct 15 '14 at 03:08