Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
bhalchandraswcg
Contributor
What do we do when the target data structure has elements from different target namespaces? The graphical mapping does not cater to multiple target namespaces when using XSDs. In this blog, I will go through a sample example and a solution to resolve this issue. Finally, I will leave you with other options to solve the same problem.

The problem is also documented in SAP Note 2888381 - [Mapping XSD] Namespace Missing in External References.

Problem Statement


Let us assume that we have this data structure:


Data Structure


Here the Invoice data type belongs to the 'http://www.example.org/Invoices' namespace and the Customer data type belongs to the 'http://www.example.org/Customers' namespace.

XML Schema Documents


These are the Schema for Invoice and Customer:

Invoices Schema


<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.example.org/Invoices"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/Invoices"
xmlns:cust="http://www.example.org/Customers">
<xsd:import schemaLocation="../xsd/Customers.xsd"
namespace="http://www.example.org/Customers" />
<xsd:element name="Invoices" type="tns:Invoices" />
<xsd:complexType name="Invoices">
<xsd:sequence>
<xsd:element name="Invoice" type="tns:Invoice" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Invoice">
<xsd:sequence>
<xsd:element name="Id" type="xsd:ID" />
<xsd:element name="BillTo" type="cust:Customer" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

Customers Schema


<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
targetNamespace="http://www.example.org/Customers"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/Customers">
<xsd:complexType name="Customer">
<xsd:sequence>
<xsd:element name="Id" type="xsd:ID" />
<xsd:element name="Name" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

Mapping


Let us assume the mapping is one-to-one. I uploaded the two schemas into Mapping Artifact and used the 'Map selected fields and sub-tree with identical names' feature to quickly map the fields.

Please note, as per SAP Note 2458816 - Using XML with reference to XSD in SOAP Receiver channel under SAP Cloud Platform..., I had to change the schemaLocation in the Invoice Schema Document to use the Customers Schema Document available in local integration resources.

The mapping looks like this:


Mapping using XSDs



Test


Input


I created one sample XML like this:
<?xml version="1.0" encoding="UTF-8"?>
<inv:Invoices xmlns:cust="http://www.example.org/Customers"
xmlns:inv="http://www.example.org/Invoices">
<inv:Invoice>
<inv:Id>INV-1234</inv:Id>
<inv:BillTo>
<cust:Id>CUS-5678</cust:Id>
<cust:Name>SAP</cust:Name>
</inv:BillTo>
</inv:Invoice>
</inv:Invoices>

Output


The mapping is one-to-one so the expected output is that the input payload will be recreated as it is. However, the namespaces of Id and Name elements of Customer are removed. This is a problem.
<?xml version="1.0" encoding="UTF-8"?>
<inv:Invoices xmlns:inv="http://www.example.org/Invoices">
<inv:Invoice>
<inv:Id>INV-1234</inv:Id>
<inv:BillTo>
<Id>CUS-5678</Id>
<Name>SAP</Name>
</inv:BillTo>
</inv:Invoice>
</inv:Invoices>

Solution


In our solution, we merged the two Schema Documents into a WSDL like so:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.example.org/Invoices"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Invoices"
targetNamespace="http://www.example.org/Invoices">
<wsdl:types>
<xsd:schema
targetNamespace="http://www.example.org/Customers"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/Customers">
<xsd:complexType name="Customer">
<xsd:sequence>
<xsd:element name="Id" type="xsd:ID" />
<xsd:element name="Name" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
<xsd:schema
targetNamespace="http://www.example.org/Invoices"
elementFormDefault="qualified"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.example.org/Invoices"
xmlns:cust="http://www.example.org/Customers">
<xsd:import namespace="http://www.example.org/Customers" />
<xsd:element name="Invoices" type="tns:Invoices" />
<xsd:complexType name="Invoices">
<xsd:sequence>
<xsd:element name="Invoice" type="tns:Invoice" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Invoice">
<xsd:sequence>
<xsd:element name="Id" type="xsd:ID" />
<xsd:element name="BillTo" type="cust:Customer" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
</wsdl:definitions>

Please note that the schemaLocation attribute is no longer required in Invoices Schema as the Customers Schema is available in the same WSDL.

Mapping


Now the mapping also shows appropriate namespaces:


Mapping with WSDL before refactoring


I will remove the old unnecessary mapping and we have our final mapping ready:


Mapping with WSDL after refactoring



Test


<?xml version="1.0" encoding="UTF-8"?>
<inv:Invoices xmlns:inv="http://www.example.org/Invoices">
<inv:Invoice>
<inv:Id>INV-1234</inv:Id>
<inv:BillTo>
<cust:Id xmlns:cust="http://www.example.org/Customers">CUS-5678</cust:Id>
<cust:Name xmlns:cust="http://www.example.org/Customers">SAP</cust:Name>
</inv:BillTo>
</inv:Invoice>
</inv:Invoices>

As expected, the namespace of Id and Name elements for the Customer are correct in the output payload.

Notes


Please note that you have to flatten all the schemas into the WSDL. It is not enough to have a root element in the WSDL.

For example, in the example discussed in this blog, having an Invoices WSDL and Customers Schema Document separately does not work.

Other Options


Other options to resolve multiple namespaces issues are:

  • XSLT Mapping

  • Groovy Script


XSLT Mapping


You can either switch to XSLT completely or use a combination of Graphical Mapping and XSLT Mapping where Graphical Mapping will do the logical mapping and XSLT Mapping will inject namespaces where necessary.

For example, we can use this XSLT Mapping to add the namespace for Customer elements:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cust="http://www.example.org/Customers"
xmlns:inv="http://www.example.org/Invoices">
<xsl:mode on-no-match="shallow-copy"></xsl:mode>
<xsl:template match="/inv:Invoices/inv:Invoice/inv:BillTo/Id">
<cust:Id>
<xsl:value-of select="/inv:Invoices/inv:Invoice/inv:BillTo/Id"></xsl:value-of>
</cust:Id>
</xsl:template>
<xsl:template match="/inv:Invoices/inv:Invoice/inv:BillTo/Name">
<cust:Name>
<xsl:value-of select="/inv:Invoices/inv:Invoice/inv:BillTo/Name"></xsl:value-of>
</cust:Name>
</xsl:template>
</xsl:stylesheet>

Groovy Script


Similarly, Groovy Script can be used in isolation or in combination with Graphical Mapping.

For example, we can use this Groovy Script to add the namespace for Customer elements:
import com.sap.gateway.ip.core.customdev.util.Message

import groovy.xml.XmlUtil

def Message processData(Message message) {

def invoices = new XmlSlurper().parse(message.getBody(Reader))

invoices.Invoice.each {
it.BillTo.Id.@xmlns = 'http://www.example.org/Customers'
it.BillTo.Name.@xmlns = 'http://www.example.org/Customers'
}

message.setBody(XmlUtil.serialize(invoices))

return message
}

References



Hope this helps,
Bala
6 Comments
Labels in this area