Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamically transform any data objects from ABAP to XML.

tobias_roye
Explorer
0 Kudos

Hey, we are currently working on an ICF Service, which calls some function modules.

The result of those calls (i.e. any export parameter) will be converted to an XML.

Unfortunately, the development of the function module is beyond our control. Therefore we do not know the type of the individual parameters.

Our goal is the following XML structure:

Any simple type is mapped to a single XML tag.

Example:


e_result TYPE string VALUE 'hello'.

will be mapped to:


<E_RESULT>hello<E_RESULT>

Structures are mapped as following (the given structure type will be in data dictionary, as it is used as export parameter):


BEGIN OF my_ddic_str

     result TYPE string

     result2 TYPE i

END OF my_ddic_str.

DATA bla TYPE my_ddic_str.

my_ddic_str-result = 'hello'.

my_ddic_str-result = 'hello2'.

will be mapped to:


<BLA result="hello" result2="hello2"/>

If any component of the structure is deep, it will be added as child tag.

Example:


BEGIN OF internal_str

     result2 TYPE string

     result3 TYPE string

END OF internal_str

BEGIN OF my_ddic_str

     result TYPE string

     internal TYPE internal_str

END OF my_ddic_str.

DATA bla TYPE my_ddic_str.

my_ddic_str-result = 'hello'.

DATA bla2 TYPE internal_str

bla2-result2 = 'hello2'.

bla2-result3 = 'hello3'.

will be mapped to:


<BLA result="hello">

     <INTERNAL result2="hello2" result3="hello3"/>

</BLA>

Any table is mapped to a main table tag, whose lines are children tags. Those children tags have "item" as name and look the same, as the structure tags (as table lines are structures).

Is there any way in XSLT or ST (Simple Transformation) to detect the type of the given data, so that we can build the XML, based on that type?
Unfortunately the identity transformation does not fit our needs, as it does not add structure's components as attributes.

Kind regards

Tobias

10 REPLIES 10

Former Member
0 Kudos

In case you want to know technical attributes of a table/type/field, here's the code you looking for:

*&- Output Workarea Type for Settings/Metadata for Output

TYPES: BEGIN OF type_mdata,

          tabname TYPE tabname.                   " Table Name

         INCLUDE TYPE abap_compdescr.             " Field Names

TYPES:   outlength TYPE i,                       " Output Length

          rollname  TYPE dd04t-rollname,          " Data Element

          reptext   TYPE dd04t-reptext,           " Header

          scrtext_s TYPE dd04t-scrtext_s,         " Short Text

          scrtext_m TYPE dd04t-scrtext_m,         " Medium Text

          scrtext_l TYPE dd04t-scrtext_l,         " Long Text

          colpos    TYPE i,                       " Column Position

          no_out    TYPE lvc_noout,               " No Out

          END OF type_mdata.

*&- Local Types

*&- Types for Elements

   TYPES: BEGIN OF type_elems,

            fldnm TYPE abap_compname,             " Field Name

            fldid TYPE dd04t-rollname,            " Data Element

            fldln TYPE i,                         " Output Length

          END OF type_elems.

*&- Local Data Declarations for Internal Tables

   DATA:

         lt_comps TYPE abap_compdescr_tab,        " Table Components

         lt_elems TYPE TABLE OF type_elems.       " Field Mappings



*&- Local Data Declarations for Objects

   DATA :

          lo_structdescr TYPE REF TO cl_abap_structdescr, " Structure Description

          lo_typedescr   TYPE REF TO cl_abap_typedescr,  " Type Description

          lo_tabledescr  TYPE REF TO cl_abap_tabledescr, " Table Description

          lo_elemdescr   TYPE REF TO cl_abap_elemdescr.  " Element Description


*&- Local Data Declarations for Error Hash

   DATA:  lx_root_error TYPE REF TO cx_root.      " Error for Object References


*&- Get Table Component Reference (IT_TABLE can be any table here)

   TRY.

       lo_tabledescr ?= cl_abap_typedescr=>describe_by_data( it_table ).

     CATCH cx_root INTO lx_root_error ##catch_all.

       CLEAR: lw_msgtx.

       lw_msgtx = lx_root_error->get_text( ).

       MESSAGE lw_msgtx TYPE c_msgty_s DISPLAY LIKE c_msgty_e .

       EXIT.

   ENDTRY.

*&- Get Type Description for Table Type

   TRY.

       lo_typedescr = lo_tabledescr->get_table_line_type( ).

     CATCH cx_root INTO lx_root_error ##catch_all.

       CLEAR: lw_msgtx.

       lw_msgtx = lx_root_error->get_text( ).

       MESSAGE lw_msgtx TYPE c_msgty_s DISPLAY LIKE c_msgty_e.

       EXIT.

   ENDTRY.

*&- Get Line Type by Type Casting to Structure Type

   lo_structdescr ?= lo_typedescr.

*&- Get Fields from the Table Components

   lt_comps = lo_structdescr->components[].

*&- Loop through the Fields and Create Field Catalog

   LOOP AT lt_comps INTO ls_comps.

*&- Get Element Details (Data Element)

     lo_elemdescr ?= lo_structdescr->get_component_type( p_name = ls_comps-name ).

*&- Store Data in Texts Table

     ls_elems-fldnm = ls_comps-name.

     ls_elems-fldid = lo_elemdescr->help_id.

     ls_elems-fldln = lo_elemdescr->output_length.

     APPEND ls_elems TO lt_elems.

     CLEAR: ls_elems.

   ENDLOOP.                             " LOOP AT lt_comps INTO ls_comps.



*&- Get Data Elements Description Texts from Table DD04T

   IF lt_elems IS NOT INITIAL.

     SELECT * FROM dd04t

              INTO TABLE lt_dd04t

              FOR ALL ENTRIES IN lt_elems

              WHERE rollname = lt_elems-fldid

                AND ddlanguage EQ sy-langu

                AND as4local = c_as4locl.

     IF sy-subrc EQ 0.

*&- Sort All Table Data Found

       SORT lt_dd04t BY rollname.

     ENDIF.                             " IF sy-subrc EQ 0.

   ENDIF.                               " IF lt_elems IS NOT INITIAL.

*&- Sort ALV Header Texts Table

   SORT t_heads BY name.

*&- Prepare Metadata

   LOOP AT lt_comps INTO ls_comps.

*&- First Store Table Index

     lw_index = sy-tabix.

*&- Get Data Element Name for Field

     READ TABLE lt_elems INTO ls_elems

                WITH KEY fldnm = ls_comps-name

                BINARY SEARCH.

     IF sy-subrc EQ 0.

       READ TABLE lt_dd04t INTO ls_dd04t

                  WITH KEY rollname = ls_elems-fldid

                  BINARY SEARCH.

       IF sy-subrc NE 0.

*&- If Nothing Found, Temporary Fill Missing Texts

         ls_dd04t-rollname = ls_elems-fldid.

         ls_dd04t-reptext = 'Missing Text'.

         ls_dd04t-scrtext_s = ls_dd04t-reptext.

         ls_dd04t-scrtext_m = ls_dd04t-reptext.

         ls_dd04t-scrtext_l = ls_dd04t-reptext.

       ENDIF.                           " IF


*&- Prepare Final Output Metadata Table

     ls_metadata-tabname = ls_control-keynm.

     ls_metadata-length = ls_comps-length.

     ls_metadata-decimals = ls_comps-decimals.

     ls_metadata-type_kind = ls_comps-type_kind.

     ls_metadata-name = ls_comps-name.

     ls_metadata-outlength = ls_elems-fldln.

     ls_metadata-rollname = ls_dd04t-rollname.

     ls_metadata-reptext = ls_dd04t-reptext.

     ls_metadata-scrtext_s = ls_dd04t-scrtext_s.

     ls_metadata-scrtext_m = ls_dd04t-scrtext_m.

     ls_metadata-scrtext_l = ls_dd04t-scrtext_l.

     ls_metadata-colpos = lw_index.

     ls_metadata-no_out = ls_heads-no_out.

*&- Clear Variables

     CLEAR: ls_metadata, ls_elems, ls_dd04t.

   ENDLOOP.                             " LOOP AT lt_comps INTO ls_comps.


By end of this loop you would have all technical information to create an XML tree. You may be required to modify the code a bit to fit your requirement. However, the interesting feature is you can get all type/technical/semantic information of an table/field.


Check if this transformation can be helpful in your case.

*&- Local Data Declarations for References

   DATA: lo_xml_writer TYPE REF TO cl_sxml_string_writer.       " XML Writer Object


*&- Create an XML Object for Internal Table

   lo_xml_writer = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10 ).

*&- Call Transformation to Convert the String to XML Object (IT_INPUT can be any table)

   CALL TRANSFORMATION id SOURCE headers = it_input RESULT XML lo_xml_writer.

Adding to this, the following classes might be helpful in creating tags elements.

if_ixml_document

if_ixml_element,

if_ixml_attribute



Cheers,

VS


0 Kudos

Hey VS, thank you very mutch for your answer.

Currently (and I should have mentioned that) we are actually using the IXML library to build the XML tree programmatically, where we use the tabledescriptors and structdescriptors to determine the technical types of our data elements. Everything works fine, but we are running into some performance issues.

When our ICF Service has to create a big XML it consumes approximately 120 ms of ressources.

When switching to the XSLT/ST approach and using the identity transformation this overall time is reduced to 60 ms.

Unfortunately I am very new to XSLT/ST. Is there a way to detect (directly within XSLT or ST), if the source object is a table or a deep structure?.

If not, I will try your approach, by using the ID transformation and then using table and struct descriptors to adjust the XML result to my needs.

Sandra_Rossi
Active Contributor
0 Kudos

I don't understand how your ICF service handler can call function modules without knowing the parameter types. If you don't pass compatible types, that will dump!

That said, if you have a data object (structured or whatever) containing the parameter value, I think it's possible to automatically map the structure components to attributes of its upper element, whatever you do it in XSLT or ST (which would be preferrable over XSLT because of performance). But if you don't master ST/XSLT, I recommend to do it in iXML as suggested by VS, after having called the identity transformation (as I said, to call the function modules, you must have data objects compatible with parameters, so you may apply the identity transformation).

0 Kudos

Hey Sandra, thanks for your response,

as you can see in my response to VS's answer, we are actually using the IXML library to parse the received XML and then use the information about the function module, we get from database table fupararef, to convert the strings from the XML to the correct type.

Unfortunately, we have some performance issues, which are not a big problem, when parsing an XML, but during XML creation (based on the result of the function module).

And yes, XSLT / ST is very new to me, indeed. But, as this is so much faster, than our IXML handling, my hope is, that it is possible to do the whole abap - xml transformation solely in XSLT (or ST, as it is faster - thanks for this information!). But I do not know, if it is possible to detect (in XSLT / ST), if the source object is a table or a deep structure (like I could use


DESCRIBE FIELD l_value TYPE l_type

to get the type in ABAP).

0 Kudos

You say "so fast", but there's not so much difference between 60/120 ms! Why making a Ferrari for one program when the rest of SAP is "so" slow? Personally, I would keep your current code.

Another point is that ID is an XSLT, so it's basically slow (although it doesn't do many things). With an ST, you may expect a faster processing.

In ST/XSLT, you may call ABAP (tt:call-method for ST, and sap:call-external and sap:external-function for XSLT)

By the way, after thinking, I'm not so sure that ST could fit your needs, because I don't know if it may work on elements with a variable name (yes for attributes). Well, that's pure theory, I'm not expert, so the best way is to try (personally I would first try with XSLT).

0 Kudos

Yes, you are right. 120ms is not that much. Unfortunately we do not know, how this duration scales, when more devices connect to our ICF Service and invoke the handler method.

We know, that sometime there will be about 500 devices connected to the ICF Service. Each device will send approximately 1000 requests a day.

We fear, that this will result in an inappropriate high load on the SAP system. Therefore, we are happy with every saved millisecond.

Thank you very much for mentioning the tt:call-method (sap:call-external) functionality.

I will have a look at that.

0 Kudos

I digged a little bit to see whether ST can be used.

I think it's not possible to use ST because of 2 reasons: the element names cannot be dynamic, and it's not possible to loop at structure components (tt:loop is only for a data node corresponding to an internal table).

About XSLT, I had done some highly dynamic transformations in the past (with XSLT for PHP, not with XSLT for ABAP), so I'm confident you could use it (xsl:element for dynamic element name, xsl:for-each may be used for structure components, and name(.) as xpath expression for getting the element name of current node)

0 Kudos

Thank you Sandra for giving these hints and xslt-statements I can use.

I will now continue diving deeper into this topic and hopefully will come up with an appropriate solution 🙂

horst_keller
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Sandra,

ID can be seen as an XSLT program in the repository, but that is not necessarily called.

In dependence from source and result, there are optimized ID-engines called instead.

Best

Horst

Clemenss
Active Contributor
0 Kudos

Hi Tobias,

I don't remember where I found the basics, I created a very simple and straightforward method to convert any kind of abap data object into xml. The result is written as a file to the users desktop. I used methods parse_string and export_to_file of class cl_xml_document but you can also use method GET_DATA and continue as you need.

METHOD xml_to_desktop." importing  IS_ANY type ANY

   DATA:

     lv_xml                                 TYPE string,

     lo_xml_doc                             TYPE REF TO cl_xml_document,

     lv_get_desktop_directory               TYPE string,

     lv_localfile                           TYPE localfile,

     lo_typedescr                           TYPE REF TO cl_abap_typedescr,

     ls_x030l                               TYPE x030l.

   lo_typedescr = cl_abap_datadescr=>describe_by_data( is_any ).

   ls_x030l     = lo_typedescr->get_ddic_header( ).


   CALL TRANSFORMATION id

      SOURCE data_node = is_any

      RESULT XML lv_xml.

   CREATE OBJECT lo_xml_doc.

   lo_xml_doc->parse_string( lv_xml ).


   cl_gui_frontend_services=>get_desktop_directory(

     CHANGING

       desktop_directory    = lv_get_desktop_directory ).

   cl_gui_cfw=>flush( ).

   lv_get_desktop_directory =

     lv_get_desktop_directory &&

     '\'                      &&

     ls_x030l-tabname         &&

     sy-datum                 &&

     sy-uzeit                 &&

     '.xml'(xml)                   .

   lv_localfile = lv_get_desktop_directory.

   lo_xml_doc->export_to_file( lv_localfile ).

ENDMETHOD.


I hope the code will do what you expect it to do - I love ist for the  simplicity


Regards Clemens