ABAP Trapdoors: CALL TRANSFORMATION id(ontwork)

This is a repost of an article published on the SAP Community Network.


Welcome to another ABAP Trapdoors article - the first one posted using my new SCN user. If you are intersted in the older articles, you can find a link list at the bottom of this post.

I've been using asXML for some years now as a convenient way to serialize and deserialize arbitrary ABAP data structures. Some time ago, I learned about IF_SERIALIZABLE_OBJECT and its use to include class instances (aka objects) in an asXML representation as well. A few days ago, I decided to use this technique in a current development project. At the same time, I was trying to use CL_DEMO_OUTPUT_STREAM instead of classic lists as suggested by the online documentation, and since I was supposedly familiar with the basics of using transformations, I focused rather on the usage of this new output technology. I hacked together a small demo programm like this one:

REPORT z_test_serialize.

CLASS lcl_serializable_thingy DEFINITION CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_serializable_object.
    METHODS constructor
      IMPORTING
        i_foo TYPE string.
    METHODS get_foo
      RETURNING
        VALUE(r_foo) TYPE string.
  PRIVATE SECTION.
    DATA g_foo TYPE string.
ENDCLASS.

CLASS lcl_serializable_thingy IMPLEMENTATION.
  METHOD constructor.
    g_foo = i_foo.
  ENDMETHOD.
  METHOD get_foo.
    r_foo = g_foo.
  ENDMETHOD.
ENDCLASS.

CLASS lcl_main DEFINITION CREATE PRIVATE.
  PUBLIC SECTION.
    CLASS-METHODS run.
ENDCLASS.

CLASS lcl_main IMPLEMENTATION.
  METHOD run.
    DATA: lr_stream        TYPE REF TO cl_demo_output_stream,
          l_foo_in        TYPE string,
          lr_first_thingy  TYPE REF TO lcl_serializable_thingy,
          l_xml_data      TYPE string,
          lr_second_thingy TYPE REF TO lcl_serializable_thingy,
          l_foo_out        TYPE string.
    lr_stream = cl_demo_output_stream=>open( ).
    SET HANDLER cl_demo_output_html=>handle_output FOR lr_stream.
    lr_stream->write_text( iv_text = 'XML Serialization of ABAP Objects Instances'
                            iv_format = if_demo_output_formats=>heading
                            iv_level  = 1 ).
    l_foo_in = |Hello, this is Foo Bar calling from { sy-sysid } client { sy-mandt }.|.
    lr_stream->write_data( iv_name  = 'Input Data'
                          ia_value  = l_foo_in
                          iv_format = if_demo_output_formats=>nonprop ).
    CREATE OBJECT lr_first_thingy
      EXPORTING
        i_foo = l_foo_in.
    CALL TRANSFORMATION id
      SOURCE instance = lr_first_thingy
      RESULT xml      = l_xml_data.
    lr_stream->write_data( iv_name  = 'XML Serialization'
                          ia_value  = l_xml_data
                          iv_format = if_demo_output_formats=>nonprop ).
    CALL TRANSFORMATION id
      SOURCE xml      = l_xml_data
      RESULT instance = lr_second_thingy.
    l_foo_out = lr_second_thingy->get_foo( ).
    lr_stream->write_data( iv_name  = 'Output Data'
                          ia_value  = l_foo_out
                          iv_format = if_demo_output_formats=>nonprop ).
    lr_stream->close( ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  lcl_main=>run( ).

Instead of the expected output (some text, an XML representation of the instance and the same text again), I got - a shortdump. The reference lr_second_thingy was not set after the second transformation - so the deserialization must somehow be broken, right? The debugger quickly revealed that the string variable that was supposed to contain the serialized instance was empty - so it's the serialization that must be broken, then, and not the deserialization? Well, they both are, in a way. To cut straight to the point, here is the faulty code:

    CALL TRANSFORMATION id
      SOURCE instance = lr_first_thingy
      RESULT xml      = l_xml_data.
    lr_stream->write_data( iv_name  = 'XML Serialization'
                           ia_value  = l_xml_data
                           iv_format = if_demo_output_formats=>nonprop ).
    CALL TRANSFORMATION id
      SOURCE xml      = l_xml_data
      RESULT instance = lr_second_thingy.

And here is the corrected version:

    CALL TRANSFORMATION id
      SOURCE instance = lr_first_thingy
      RESULT XML        l_xml_data.
    lr_stream->write_data( iv_name  = 'XML Serialization'
                           ia_value  = l_xml_data
                           iv_format = if_demo_output_formats=>nonprop ).
    CALL TRANSFORMATION id
      SOURCE XML        l_xml_data
      RESULT instance = lr_second_thingy.

Yup, the difference is a single character - or two characters in this case. Without the equals sign, XML is treated as a keyword to denote a variable containing the raw XML data. With the equals sign, something else happens that I have yet to find a sensible and practical use for - at least when used with the identity transformation. You can spot this issue if you use the pretty printer to convert the keywords to upper case - and if you notice the tiny difference between xml and XML.
 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer