Saturday, October 6, 2012

Power of the Core : Using XSD for XML Transformation in ABAP


I've already written a few posts on how to use the graphical editor to create Simple Transformations but I guess that's not good enough for lazy developers like me.

So now we are going to try a different approach assuming we already have the XSD file that describes the structure of the required XML.

Let's say you have an XSD file for which you want to generate/transform XML <-> ABAP. Of course you don't want to create the corresponding ABAP Structures or Simple Transformations manually neither you are interested in hearing about capability of another SAP Product in this field e.g. SAP PI/CE etc. That said ..

Now SAP already has this web service Wizard (in ECC6.0 / 5.0 ) that could upload WSDL / XSD files to create ABAP structures, generate related simple transformation code and a Proxy class/method for the purpose of SOAP Payload. So in a sense, a framework already exists which solves a problem that is almost a superset of what we are trying to achieve.

In short, we'll create a dummy WSDL that will just help in importing the XSD file through this SAP web service wizard. Though we are not interested in SOAP Web service here but we'll use it to cheat the wizard.

And so the experiment begins:

Step 1 : Create a dummy WSDL to cheat the web service tool :


UPDATE : Please skip this step as you can simply use the standard program SPROX_XSD2WSDL ( screenshot below ) to download a dummy WSDL file for the given XSD :) . In fact, using methods in class CL_PROXY_TEST_UTILS even the 2nd step can be automated. But I'll update that later.



------>Ignore details below for this step and move on to next step to upload the wsdl and xsd.
You can copy the template WSDL code in notepad and save it as dummy.wsdl in the same directory of your PC where you have your XSD file.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="[[ROOT]]WS" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:examples="[[XSDNamespaceURL]]"  
xmlns:tns="http://www.example.com/WSDL/[[ROOT]]" 
targetNamespace="http://www.example.com/WSDL/[[ROOT]]">

<!-- sapblog.rmtiwari.com WSDL Template for XSD import in ABAP -->

 <wsdl:types>
  <xsd:schema>
   <xsd:import namespace="[[XSDNamespaceURL]]" schemaLocation="[[XSDName]]"/>
  </xsd:schema>
 </wsdl:types>

 <wsdl:message name="[[ROOT]]Message">
  <wsdl:part name="[[ROOT]]" element="examples:[[ROOT]]"/>
 </wsdl:message>

 <wsdl:portType name="[[ROOT]]Type1">
  <wsdl:operation name="Process[[ROOT]]">
   <wsdl:input name="[[ROOT]]" message="tns:[[ROOT]]Message"/>
  </wsdl:operation>
 </wsdl:portType>

</wsdl:definitions>
Now use notepad's replace all to adjust the template as below : 

  1. Adjust Namespace URLs as per XSD : For this just replace [[XSDNamespaceURL]] with the actual namespace from the XSD. e.g. In my case ( see XSD given below ), I will replace the [[XSDNamespaceURL]] with http://www.example.com/XMLSchema
  2. XSD File Name to be imported : Also replace [[XSDName]] with the name of your xsd file. e.g. in my case I will replace [[XSDName]] with AccessRequest.xsd 
  3. Root Node Name of the XML : Replace the occurrences of [[ROOT]] in template with the root node name of the XSD/XML. In my case, if you see the XSD file below, the name of the root node is AccessRequest
In my case, the XSD is as below. I have chosen a simple one to explain the idea.

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.example.com/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:example="http://www.example.com/XMLSchema" elementFormDefault="unqualified">
 <xsd:element name="AccessRequest">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element name="UserId" type="xsd:string"/>
    <xsd:element name="Password" type="xsd:string"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
</xsd:schema> 
So after replacing the names/namespace etc., my final dummy WSDL looks like this :

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="AccessRequestWS" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:examples="http://www.example.com/XMLSchema"  
xmlns:tns="http://www.example.com/WSDL/AccessRequest"
targetNamespace="http://www.example.com/WSDL/AccessRequest">
 <wsdl:types>
  <xsd:schema>
   <xsd:import namespace="http://www.example.com/XMLSchema" schemaLocation="AccessRequest.xsd"/>
  </xsd:schema>
 </wsdl:types>

 <wsdl:message name="AccessRequestMessage">
  <wsdl:part name="AccessRequest" element="examples:AccessRequest"/>
 </wsdl:message>

 <wsdl:portType name="AccessRequestType1">
  <wsdl:operation name="ProcessAccessRequest">
   <wsdl:input name="AccessRequest" message="tns:AccessRequestMessage"/>
  </wsdl:operation>
 </wsdl:portType>

</wsdl:definitions>
So Now we have our WSDL and XSD files ready to be uploaded in SAP.

Step 2 : Upload the WSDL and XSD files through SAP Web Service Wizard :


This is pretty much standard : in Transaction SE80 --> Enter Package Name (or $TMP ) --> Start the Web Service Wizard .

The wizard will take you through the steps to upload the WSDL file which in turn will automatically find the XSD file in the same directory and upload that as well.

I've used $TMP but you should use a suitable package if you want to transport it later.


So once uploaded, you'll have the proxy class and related ABAP structures as well as underlying simple transformations generated for you. Don't forget to activate the proxy/structures.

Actually you don't really needs proxies for our purpose but just the structures. It won't do any harm to keep the proxy as well.


Step 3 : Write your ABAP program to generate XML :

Note that you need the message type structure name (e.g. 'ZTESTRAMACCESS_REQUEST_MESSAGE' ) that was generated in last step to use it in your own program.

Also I've used an optional step to remove the namespace information from the generated XML. For that you'll need to create an XSLT in XSLT_TOOL. I've got this XSLT from here : http://wiki.tei-c.org/index.php/Remove-Namespaces.xsl

XSLT Code [ Z_RAM_REMOVE_NAMESPACEE_XSLT ] to remove the namespace from final xml is as below and it's called from the ABAP program.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no"/>

<xsl:template match="/|comment()|processing-instruction()">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

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

<xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Finally the ABAP Program is as below :

 
REPORT  z_ram_test_xsd_xml.
*------------------------------------------------------------------
* By :  www.rmtiwari.com on 30.09.2012
*------------------------------------------------------------------
DATA : lv_datatype TYPE prx_r3name.
DATA : lv_xml      TYPE xstring,
       ls_abap_data TYPE ZTESTRAMACCESS_REQUEST_MESSAGE.

* Which root data type ?
lv_datatype = 'ZTESTRAMACCESS_REQUEST_MESSAGE'.

*------- Populate abap Data structure------------------------------
ls_abap_data-user_id = 'test_user'.
ls_abap_data-password = 'test_password'.
*------------------------------------------------------------------
TRY.
*   This will generate the Simple Transformation from information
*   available in proxy tables and transform the abap input data
*   to XML with *namespace*.
    lv_xml = cl_proxy_xml_transform=>abap_to_xml_xstring(
      abap_data  = ls_abap_data
      ddic_type  = lv_datatype
*      ext_xml    = ''
      xml_header = 'full'
    ).
*------------------------------------------------------------------
*   In case you need to strip the namespace information from tags
*   then create a simple transformation Z_RAM_REMOVE_NAMESPACEE_XSLT
*   in XSLT_TOOL use the statement below.
    CALL TRANSFORMATION z_ram_remove_namespacee_xslt
    SOURCE XML lv_xml
    RESULT XML lv_xml.
*------------------------------------------------------------------
*   Just to show the xml .
    cl_srtg_helper=>write_utf8_xmldoc(
      doc = lv_xml
      use_html_control = 'X'
    ).
*------------------------------------------------------------------
  CATCH cx_transformation_error .
  CATCH cx_root .                                        "#EC CATCH_ALL

ENDTRY.


And now if you run your program, then may be you'll have your XML like I did :)

Not to mention that I've not verified this approach or tested it thoroughly yet. However, conceptually, I don't see much problem with it .

So if you have a really big XML/XSD to operate on then this could be really useful.

Further, looks like we can also upload multiple XSD files in one upload by changing the wsdl to RPC style with multiple imports & parts.

Warning : Please don't use above approach for any productive program without proper testing. 

Also, just wanted to mention that Blogger's comment-section does not accept XML/HTML code hence you may need to escape any code before posting it in comments. [ Hint : search for "escape html tool online" in Google ]

16 comments:

  1. Hello there,

    Is it possible to use XSLT tool to convert w3c xml to HL7?

    Thanks,
    yP

    ReplyDelete
    Replies
    1. Sorry there is not much context to answer your one liner..

      if you've HL7 V3 which seems to be some XML variant itself then you can convert that XML to another XML document using ST and/or XSLT. However writing those ST/XSLTs manually or through any utility will require skill & effort.

      Thanks,
      Ram

      Delete
  2. Hello There,
    in your blog above you mention that a large XSD can be uploaded into one wsdl using rpc style. i have 3 related xsd to be imported .how can i import them using rpc style? please.help
    thanks in advance..

    ReplyDelete
    Replies
    1. Just to clarify this post is about *using* web service related tools to generate simple transformations for normal ( not necessarily web service related ) XML transformations.
      In the post, the example is given for one XSD.

      --------
      Having said that I suggest you take a look at "OSS Note 1714792 - Proxy generation terminated: Message must have exactly one part".

      web service wizard/tool to generate the proxy, supports following ( Refer to OSS Note ):

      WSDL message with several parts (RPC-style only) are supported from 7.0 SP14.

      You could see details about it and other supported XML Schema and WSDL in a list attached in SAP Note 944029 XML schema supported by ABAP proxy generation.

      WSDL Elements message with several parts (RPC-style only).

      ABAP Proxies 640 / 700 up to SP13 Not Supported.
      ABAP Proxies 700 from SP14 / 710 Supported.

      Document style WSDL with multiple parts per message are not supported

      >>>

      So, it's possible to use the web service proxy generation wizard to upload RPC style WSDL that includes multiple XSDs.

      However, if you don't have the RPC style WSDL but just the 3 XSDs then you'll have to create the RPC style WSDL that includes all 3 XSDs. Further, if you are not dealing with a web service but just want the STs to be generated through the proxy wizard then there will be more work for you to adjust the XMLs generated through the Proxy.

      Thanks,
      Ram


      Delete
    2. ..Just to add that multiple XSDs can be included in Document Style WSDL too however "multiple parts per message are not supported".

      Thanks,
      Ram

      Delete
  3. Very nice post, how can I get just the simple transformation after following these steps?

    ReplyDelete
    Replies
    1. try this ..

      Get the NAME from table SPROXXSL for generated Z Proxy class & Method.
      This is not the complete name of generated transformation but if you search with pattern *NAME* in STRANS or XSLT_TOOL transaction, you can find the generated transformation. complete name should be like /1SAI/TX for OUTPUT structure...

      Thanks,
      Ram

      Delete
    2. My proxy class don't generated entries in SPROXXSL,
      What can i do?

      Delete
  4. Hello.
    Thank you for your detailed instructions!
    But I tried to follow them and get the error during Service Consumer creation:
    "Incorrect value: Schema w/o target namespace must not contain any subelements, except xsd:import/xsd:include/xsd:annotation"

    Can you publish content of AccessRequest.xsd file?

    ReplyDelete
    Replies
    1. ....I guess you need a target namespace in your schema . Regarding content of AccessRequest.xsd - it's already their in the post : look for XML code having targetNamespace="http://www.example.com/XMLSchema highlighted in Red.

      Just to clarify that this is an experimental way for parsing XML and I've not done much exploration any further on this. So please see if it provides you the desired results...otherwise you can go for creating simple transformation program using graphical editor as explained in Generate Simple Transformation for XML in ABAP - Part II and other related posts.

      Thanks,
      Ram

      Delete
  5. Hi, I've found your post very useful for my purposes, but I need to save the generated XML and the save button apears to be inactive. How can I save the data?

    Thanks!

    ReplyDelete
    Replies
    1. ..basically you need to export the XML file rather than show it on screen - please check other blogs on the topic http://sapblog.rmtiwari.com/search/label/XML and you'll find several methods to export the XML as file on your local workstation . e.g.
      cl_proxy_service=> download_payload(
      file_x = gv_file_name
      payload = gv_xml_xstring
      ).

      However, if you want to export as a file on SAP application server then you may need to use OPEN DATASET to transfer the content in a file on app server.

      Thanks,
      Ram

      Delete
  6. I was reviewing a recent post of mine in SCN and just found out that the SAP Note Enhancer extension was not the first time I referenced your work:
    http://scn.sap.com/community/abap/blog/2015/05/26/xml-schema-xsd-validation-in-abap-with-limitations

    ReplyDelete
    Replies
    1. ..glad to see you are adapting for new use-cases and extending it further :)

      Cheers,
      Ram

      Delete
  7. Thanks, it's really helpful!
    But now I have some problem.
    I need to have comma "," instead of dot "." in numerical values in my output XML file.
    I don't want to replace it in result XML, I prefer to do it in XSL transformation.
    So I changed ABAP data type for generated proxy elements from DEC 18.2 to STRING and I'm filling my "numbers" with "123,45" string.
    But now CALL TRANSFORMATION results in error message: "An error occurred during serialization in the simple transformation program /1SAI/SAS29EB1DE03BB3874AA2B2".
    Anyone knows what is the solution to this problem?

    ReplyDelete

Info : Comments are reviewed before they are published. SPAMs are rejected.

Copyright