Skip to Content
Technical Articles
Author's profile photo Bhalchandra Wadekar

Multiple Target Namespaces in Graphical Mapping

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%20Structure

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 Integration, 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%20using%20XSDs

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%20with%20WSDL

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

Assigned Tags

      6 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Pedro Cardoso
      Pedro Cardoso

      This is Integration Magic Act #54 from the one and only Bhalchandra Wadekar!

      Very creative solution to a problem that is not an edge case, but a frustrating OOTB restriction today. Bravo!!

      Author's profile photo Bhalchandra Wadekar
      Bhalchandra Wadekar
      Blog Post Author

      Thank you, Pedro 🙂

      Author's profile photo Saurabh Kumbhare
      Saurabh Kumbhare

      Hi Bhalchandra,

       

      I am unable to add the schemas to CPI.

       

      I get the error "Cannot import file 'Invoices.xsd'; XSD: Type reference 'http://www.example.org/Customers#Customer' is unresolved".

      Where could I have gone wrong ?

       

      THanks

      Riaan

      Author's profile photo Bhalchandra Wadekar
      Bhalchandra Wadekar
      Blog Post Author

      Hi Riaan Kumbhare,

      Did you import the Customers.xsd first? You need to import the dependent schemas before importing the main schema.

      Hope this helps,
      Bala

      Author's profile photo fei lu
      fei lu

      Hi Bhalchandra,

       

      Thanks for your article , it helps me to solve a similar issue .

       

      Author's profile photo Bhalchandra Wadekar
      Bhalchandra Wadekar
      Blog Post Author

      I am glad it helped 🙂