- Working with XML and JSON in ABAP
This cheat sheet provides a high-level overview of working with XML and JSON in ABAP. It covers the following topics:
- Processing XML using class libraries (iXML, sXML)
- XML Transformations using XSLT and Simple Transformations (ST), i.e. serializations (ABAP to XML) and deserializations (XML to ABAP)
CALL TRANSFORMATION
syntax- Working with JSON data
💡 Note
- The cheat sheet snippets and the executable example cover simple cases. Find more executable examples of the ABAP Keyword Documentation following the links in the More Information section.
- For more detailed information, such as documentation on ST syntax, what asXML is, etc., also see the links in the More Information section.
- Integrated XML Library
- Provides methods for ...
- validating and parsing (i.e. reading) XML data in 1.0 format into a DOM representation (Document Object Model, which is a tree-like representation of the documents in the memory)
- processing the DOM representation (you can create or edit XML data)
- rendering (i.e. writing) the DOM representation
- To access XML data, you need input and output streams that are created using iXML methods.
- Advantages: Easy access to the individual parts of an XML document possible, DTDs (Document Type Definitions) are possible
- Disadvantages: High memory consumption of the DOM (if the complete DOM is created)
The following code snippets demonstrate a selection of iXML methods for handling XML data. Note that the snippets use classes available for ABAP for Cloud Development. Find documentation on the classes for classic ABAP here (F1 documentation for Standard ABAP).
Creating XML data using iXML:
"Creating one factory object of the access class cl_ixml_core using the
"create method. It is used to access the iXML library.
DATA(ixml_cr) = cl_ixml_core=>create( ).
"Creating an XML document stored in DOM format in the memory
DATA(document_cr) = ixml_cr->create_document( ).
"Creating DOM nodes step by step. Here, elements, attributes, and content are
"and inserted into the XML. Using an appropriate attribute setting, the result
"represents XML data in the asXML format (see more information further down).
"Creating a root node
DATA(root) = document_cr->create_element_ns( name = 'abap'
prefix = 'asx' ).
root->set_attribute_ns( name = 'asx'
prefix = 'xmlns'
value = 'http://www.sap.com/abapxml' ).
root->set_attribute_ns( name = 'version'
value = '1.0' ).
document_cr->append_child( root ).
DATA(xml_node1) = document_cr->create_element_ns( prefix = 'asx'
name = 'values' ).
root->append_child( xml_node1 ).
DATA(xml_node2) = document_cr->create_element_ns( name = 'STRING' ).
xml_node1->append_child( xml_node2 ).
xml_node2->append_child( document_cr->create_text( 'Hello World' ) ).
"Creating a renderer for rendering the XML document into an output stream
"that is used to pass XML data from the renderer.
DATA xml_doc TYPE xstring.
ixml_cr->create_renderer( document = document_cr
ostream = ixml_cr->create_stream_factory( )->create_ostream_xstring( string = xml_doc )
)->render( ).
"Result (xstring converted to string):
"<?xml version="1.0"?><asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
"<asx:values><STRING>Hello World</STRING></asx:values></asx:abap>
Parsing XML data using iXML:
"Creating demo XML data to be used in the example as string. Using the cl_abap_conv_codepage
"class, you can convert the string to xstring which is required for the example.
DATA(some_xml) = cl_abap_conv_codepage=>create_out( )->convert(
`<hi>` &&
` <word1>hallo</word1>` &&
` <word2>how</word2>` &&
` <word3>are</word3>` &&
`</hi>` ).
"Creating one factory object of the access class cl_ixml_core using the
"create method. It is used to access the iXML library.
DATA(ixml_pa) = cl_ixml_core=>create( ).
"Creaing an input stream that is used for the input of XML data
DATA(stream_factory_pa) = ixml_pa->create_stream_factory( ).
"Creating an XML document stored in DOM format in the memory
DATA(document_pa) = ixml_pa->create_document( ).
"Creating a parser. It requires the following input parameters: input stream to be parsed,
"the XML document to which the stream is parsed, a factory required to create a stream
DATA(parser_pa) = ixml_pa->create_parser(
istream = stream_factory_pa->create_istream_xstring( string = some_xml )
document = document_pa
stream_factory = stream_factory_pa ).
"Parsing XML data to a DOM representation in one go. It is put in the memory.
"Note: You can also parse sequentially, and not in one go.
DATA(parsing_check) = parser_pa->parse( ).
IF parsing_check = 0. "Parsing was successful
"--------------------- Directly reading nodes ----------------------
"Accessing the root element of the DOM. It can be used as the initial node
"for accessing subnodes.
"Note: Multiple methods are available to further process the nodes.
DATA(root_element) = document_pa->get_root_element( ).
"First subnode
DATA(child_element) = root_element->get_first_child( ).
"Gettng the value of that node
DATA(child_element_value) = child_element->get_value( ). "hallo
"Next adjacent node/getting the value
DATA(next_element_value) = child_element->get_next( )->get_value( ). "how
"Direct access using element names
"Result: First element searched for
DATA(element_by_name) = document_pa->find_from_name( name = `word3` )->get_value( ). "are
"--------------------- Reading using iterators ----------------------
"i.e. going over the XML nodes one after another
"Creating an iterator
DATA(iterator_pa) = document_pa->create_iterator( ).
DO.
"For the iteration, you can use the get_next method to process the nodes one after another.
"Note: Here, all nodes are respected. You can also create filters to go over specific nodes.
DATA(node_i) = iterator_pa->get_next( ).
IF node_i IS INITIAL.
"Exiting the loop when there are no more nodes to process.
EXIT.
ELSE.
... "Do something, e.g. modify values
ENDIF.
ENDDO.
"Creating a renderer. It renders the XML document into an output stream
"that is used to pass XML data from the renderer.
DATA xml_pa TYPE xstring.
ixml_pa->create_renderer( document = document_pa
ostream = ixml_pa->create_stream_factory( )->create_ostream_xstring( string = xml_pa )
)->render( ).
...
ELSE.
"Parsing was not successful.
...
ENDIF.
- Serial XML Library
- Provides XML readers and writers for different sources and targets
- Processes XML data sequentially
- Allows you to parse and render XML data in a token-based (iterating across all nodes, i.e. tokens, in the tree structure of the XML data) and object-oriented way (wrapping methods of the token-based access; providing an object-oriented way to access XML nodes)
- Unlike iXML, ...
- more formats are possible (standard XML 1.0, but also XOP, binary XML, and JSON).
- no document is created in DOM format (if you do not need a DOM representation and DTDs, sXML is a more performant alternative to iXML).
Creating XML data using sXML:
"--------------------- Token-based rendering ----------------------
"For sXML, there are specialized writer classes, such as CL_SXML_STRING_WRITE.
"Writers created with this class render XML data to a byte string.
"The XML 1.0 format and UTF-8 are used by default in the create method.
"Here, the parameters are specified explicitly.
"Note: The interface IF_SXML_WRITER contains the components that are valid
"for all readers (the abstract super class CL_SXML_WRITER includes this
"interface as well as implementations for all readers). In the example below,
"a cast is required so as to access special methods (such as open_element).
DATA(writer) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10
encoding = 'UTF-8' ) ).
"Creating/Rendering XML data using token-based rendering
"The example shows shows how XML data can be created using various available methods.
"Here, token-based rendering is used, i.e. each node is added to the XML data using
"a method. There is a method for each node type.
TRY.
"Creating nodes (the order of the nodes is crucial)
writer->open_element( name = 'flights' ).
writer->open_element( name = 'flight' ).
writer->open_element( name = 'carrier' ).
writer->write_value( 'LH' ).
writer->close_element( ).
writer->open_element( name = 'flightnumber' ).
writer->write_value( '400' ).
writer->close_element( ).
writer->close_element( ).
writer->open_element( name = 'flight' ).
writer->open_element( name = 'carrier' ).
writer->write_value( 'DL' ).
writer->close_element( ).
writer->open_element( name = 'flightnumber' ).
writer->write_value( '1984' ).
writer->close_element( ).
writer->close_element( ).
writer->close_element( ).
CATCH cx_sxml_state_error.
...
ENDTRY.
"The XML data can be retrieved with the GET_OUTPUT method.
"Also here, a cast is required.
"The result is of type xstring.
DATA(xml) = CAST cl_sxml_string_writer( writer )->get_output( ).
"The result looks like this (when converted to string and indented):
"<?xml version="1.0" encoding="utf-8"?>
"<flights>
" <flight>
" <carrier>LH</carrier>
" <flightnumber>400</flightnumber>
" </flight>
" <flight>
" <carrier>DL</carrier>
" <flightnumber>1984</flightnumber>
" </flight>
"</flights>
"--------------------- Object-oriented rendering ----------------------
DATA(writer_oo) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10
encoding = 'UTF-8' ) ).
TRY.
writer_oo->write_node( writer_oo->new_open_element( name = 'flights' ) ).
writer_oo->write_node( writer_oo->new_open_element( name = 'flight' ) ).
writer_oo->write_node( writer_oo->new_open_element( name = 'carrier' ) ).
DATA(val) = writer_oo->new_value( ).
val->set_value( 'AZ' ).
writer_oo->write_node( val ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
writer_oo->write_node( writer_oo->new_open_element( name = 'flightnumber' ) ).
val = writer_oo->new_value( ).
val->set_value( '788' ).
writer_oo->write_node( val ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
writer_oo->write_node( writer_oo->new_open_element( name = 'flight' ) ).
writer_oo->write_node( writer_oo->new_open_element( name = 'carrier' ) ).
val = writer_oo->new_value( ).
val->set_value( 'JL' ).
writer_oo->write_node( val ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
writer_oo->write_node( writer_oo->new_open_element( name = 'flightnumber' ) ).
val = writer_oo->new_value( ).
val->set_value( '407' ).
writer_oo->write_node( val ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
writer_oo->write_node( writer_oo->new_close_element( ) ).
CATCH cx_sxml_state_error.
...
ENDTRY.
DATA(xml_oo) = CAST cl_sxml_string_writer( writer_oo )->get_output( ).
"The result looks like this (when converted to string and indented):
"<flights>
" <flight>
" <carrier>AZ</carrier>
" <flightnumber>788</flightnumber>
" </flight>
" <flight>
" <carrier>JL</carrier>
" <flightnumber>407</flightnumber>
" </flight>
"</flights>
Parsing XML data using sXML:
"--------------------- Token-based parsing ----------------------
"Creating reader
"Note: See the comments for the writer above. It is similar here. For readers,
"the interface IF_SXML_READER exists. In this example, no special methods
"are used. Therefore, a cast is not carried out.
"The example uses the XML data from above.
DATA(reader) = cl_sxml_string_reader=>create( xml_oo ).
"DATA(reader_cast) = CAST if_sxml_reader( cl_sxml_string_reader=>create( xml_oo ) ).
"To iterate accros all nodes, you can call the NEXT_NODE method.
TRY.
DO.
"Check out other available methods in ADT by placing the cursor behind '...->'
"and choose CTRL + Space.
reader->next_node( ).
"Exiting the loop when reaching the end of the XML data.
IF reader->node_type = if_sxml_node=>co_nt_final.
EXIT.
ENDIF.
"You can access the properties of the node directly.
DATA(node_type) = reader->node_type. "Node type, see the interface if_sxml_node
DATA(prefix) = reader->prefix. "Namespace prefix
DATA(name) = reader->name. "Name of the element
DATA(value_type) = reader->value_type. "Value type, see the interface if_sxml_value
DATA(value) = reader->value. "Character-like value (if it is textual data)
DATA(value_raw) = reader->value_raw. "Byte-like value (if it is raw data)
...
ENDDO.
CATCH cx_sxml_parse_error.
...
ENDTRY.
"--------------------- Object-oriented parsing ----------------------
"Creating demo XML data to be used in the example.
DATA(xml_oo_read) = cl_abap_conv_codepage=>create_out( )->convert(
`<?xml version="1.0"?>` &&
`<node attr_a="123">` &&
` <subnode1>` &&
` <status>A</status>` &&
` <date format="mm-dd-yyyy">01-01-2024</date>` &&
` </subnode1>` &&
` <subnode2>` &&
` <text attr_b="1" attr_c="a">abc</text>` &&
` <text attr_b="2" attr_c="b">def</text>` &&
` <text attr_b="3" attr_c="c">ghi</text>` &&
` </subnode2>` &&
`</node>` ).
"Creating reader
DATA(reader_oo) = cl_sxml_string_reader=>create( xml_oo_read ).
TRY.
DO.
"To iterate accros all nodes, you can call the READ_NEXT_NODE method.
"When the end of the XML data is reached, the returned value is initial.
DATA(node_oo) = reader_oo->read_next_node( ).
IF node_oo IS INITIAL.
EXIT.
ENDIF.
"In object-oriented parsing, methods for token-based parsing are wrapped.
"An object-oriented access to the node is provided.
"References to objects that represent the current node are returned.
"Getting the node type
DATA(n_type) = node_oo->type.
"When the parser is currently on the node of an element opening,
"the node object has the class CL_SXML_OPEN_ELEMENT that implements the
"interface IF_SXML_OPEN_ELEMENT. With the methods included, you can
"access the XML attributes of the element, e.g. using the GET_ATTRIBUTES
"method to put the references for all attributes into an internal table.
"To access the attributes, a downcast is required.
IF n_type = if_sxml_node=>co_nt_element_open.
DATA(attributes) = CAST if_sxml_open_element( node_oo )->get_attributes( ).
...
ENDIF.
...
ENDDO.
CATCH cx_sxml_parse_error.
...
ENDTRY.
To perform transformations in ABAP, you can use:
- XSL Transformations (XSLT)
- Repository objects written in XSLT
- XSLT can process ABAP data because ABAP data is first implicitly transformed into the asXML format. The result of this implicit transformation (the asXML format) is then used as the actual source for the XSL transformation.
- Identity transformations
- Predefined XSL transformation with the name
ID
- Used to read and write the asXML (and also asJSON) format.
- When the transformation ID is called, the resulting intermediate formats (asXML, asJSON) are the direct output.
- When used, for example, to transform ABAP data, such as a structure, to XML, the transformation does not change the structure itself. You can, however, change the structure by implementing your own XSLT.
- Predefined XSL transformation with the name
- Simple Transformation (ST)
- Repository objects written in an SAP-specific programming language for transformations between XML formats and ABAP data
The tranformations are called using CALL TRANSFORMATION
statements.
Possible transformations, some of which are covered in the example:
Transformation | XSLT | ST |
---|---|---|
XML <-> XML | X | - |
ABAP <-> XML | X | X |
ABAP <-> ABAP | X | - |
💡 Note
- asXML:
- ABAP Serialization XML
- Describes a format of XML data created when serializing ABAP data (ABAP -> XML) with the identity transformation
- This format is a prerequisite for deserializations (XML -> ABAP) using identity transformations.
- Used as an intermediate format that defines a mapping between ABAP data and XML
- Therefore, if you want to deserialize XML data in ABAP, you must first transform it to the asXML format.
- Make sure that you use appropriate exception classes for potential errors in transformations. See the section Catchable Exceptions in the
CALL TRANSFORMATION
topic.- For serializing instances of classes, find more information here in the ABAP Keyword Documentation. The classes must implement the
IF_SERIALIZABLE_OBJECT
interface (find a demo in the executable example and further down).
The following code snippets demonstrate a selection of possible syntax options when using CALL TRANSFORMATION
statements.
💡 Note
You can also transform ABAP to and from JSON data using transformations. Find examples in the Transforming JSON Data Using Transformations section.
Specifying transformations
"Specifying the name of an XSL or Simple Transformation statically
CALL TRANSFORMATION zsome_transformation SOURCE ...
RESULT ...
"Specifying the identity transformation with the predefined name ID
CALL TRANSFORMATION id SOURCE ...
RESULT ...
"Dynamic specification of the transformation
"If the transformation does not exist, an exception is raised.
TRY.
CALL TRANSFORMATION ('ID') SOURCE ...
RESULT ...
CATCH cx_invalid_transformation.
...
ENDTRY.
Specifying the source of the transformation
There are multiple options. Check the executable example to explore them.
"--------------------- Transforming XML data ----------------------
"Options for src:
"- Data object of type string or xstring containing XML data in XML 1.0 format
"- Standard table with flat character-like or byte-like line type
"- Some references to iXML and sXML libraries are possible. For example, an
" interface reference variable of type if_sxml_reader.
CALL TRANSFORMATION ... SOURCE XML src
RESULT ...
"--------------------- Transforming ABAP data ----------------------
"No XML specified after SOURCE
"Options after SOURCE:
"- One or multiple ABAP data objects (abap1 in the snippet) can be specified as
" static parameter list, e.g. z1 = abap1 z2 = abap2 ...
"- When XSLT is used (such as ID), the data is serialized to asXML depending on
" the target
"- z1 etc. stands for names of XML elements
CALL TRANSFORMATION ... SOURCE z1 = abap1 ...
RESULT ...
"Dynamic specification of ABAP data objects in an internal table of type
"abap_trans_srcbind_tab
DATA(srctab) = VALUE abap_trans_srcbind_tab( ( name = 'A' value = REF #( b ) ) ( ... ) ).
CALL TRANSFORMATION ... SOURCE (srctab)
RESULT ...
Specifying the result of the transformation
There are multiple options. Check the executable example to explore them in action.
"--------------------- Transforming to XML data ----------------------
"Options for res:
"- Data object of type string or xstring
"- Data object declared inline (e.g. DATA(a) or FINAL(b)), which has then the type xstring
"- Standard table with flat character-like or byte-like line type
"- References for objects in iXML and sXML (e.g. sXML writers)
CALL TRANSFORMATION ... SOURCE ...
RESULT XML res.
"--------------------- Transforming to ABAP data ----------------------
"No XML specified after RESULT
"Similar to above, multiple ABAP data objects can be specified as a static parameter list.
CALL TRANSFORMATION ... SOURCE ...
RESULT y1 = abap1 ...
"An internal table of type abap_trans_resbind_tab can be specified.
DATA(restab) = VALUE abap_trans_resbind_tab( ( name = 'A' value = REF #( b ) ) ( ... ) ).
CALL TRANSFORMATION ... SOURCE ...
RESULT (restab).
💡 Note
More additions are available such asPARAMETERS
(for parameter binding) andOPTIONS
(for predefined transformation options). See the details in the ABAP Keyword Documentation.
The following example explores various syntax options for CALL TRANSFORMATION
statements, using the predefined identity transformation ID
.
To try the example out, create a demo class named zcl_demo_abap
and paste the code into it. After activation, choose F9 in ADT to execute the class. The example is set up to display output in the console.
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
TYPES: BEGIN OF line,
char TYPE c LENGTH 3,
int TYPE i,
END OF line,
t_type TYPE TABLE OF line WITH EMPTY KEY.
DATA(tab) = VALUE t_type( ( char = 'aaa' int = 1 )
( char = 'bbb' int = 2 )
( char = 'ccc' int = 3 ) ).
out->write( `ABAP <-> XML using XSLT (Using the Predefined Identity Transformation ID)` ).
**********************************************************************
out->write( `-------- Transforming ABAP data: ... SOURCE ... --------` ).
"This section covers syntax options after SOURCE without XML.
"ABAP (specifying ABAP data object) -> asXML/xstring
"itab stands for the name of the XML element (check ... <ITAB> ... in the output)
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML DATA(xml_tab_a).
DATA(xml_a) = cl_abap_conv_codepage=>create_in( )->convert( xml_tab_a ).
out->write( data = xml_a name = `xml_a` ).
"ABAP (specifying ABAP data objects dynamically using an internal table
"of type abap_trans_srcbind_tab) -> asXML/xstring
DATA(srctab) = VALUE abap_trans_srcbind_tab( ( name = 'ITAB' value = REF #( tab ) ) ).
CALL TRANSFORMATION id SOURCE (srctab)
RESULT XML DATA(xml_tab_b).
DATA(xml_b) = cl_abap_conv_codepage=>create_in( )->convert( xml_tab_b ).
out->write( data = xml_b name = `xml_b` ).
**********************************************************************
out->write( `-------- Transforming XML data: ... SOURCE XML ... --------` ).
"This section covers syntax options after SOURCE XML.
"Apart from data objects of type string or xstring containing XML data
"in XML 1.0 format, standard tables with flat character-like or byte-like
"line type, you can also specify certain references to iXML and sXML libraries
"sxml/ixml references as source
"The examples use XML data from above
DATA(sxml_reader) = cl_sxml_string_reader=>create( xml_tab_a ).
CALL TRANSFORMATION id SOURCE XML sxml_reader
RESULT XML DATA(xml_tab_c).
DATA(xml_c) = cl_abap_conv_codepage=>create_in( )->convert( xml_tab_c ).
out->write( data = xml_c name = `xml_c` ).
DATA(ixmlr) = cl_ixml_core=>create( ).
DATA(ixml_doc1) = ixmlr->create_document( ).
DATA(ixml_cr_streamr) = ixmlr->create_stream_factory( ).
DATA(ixml_i_stream) = ixml_cr_streamr->create_istream_xstring( xml_tab_a ).
CALL TRANSFORMATION id SOURCE XML ixml_i_stream
RESULT XML DATA(xml_tab_d).
DATA(xml_d) = cl_abap_conv_codepage=>create_in( )->convert( xml_tab_d ).
out->write( data = xml_d name = `xml_d` ).
**********************************************************************
out->write( `-------- Transforming to XML data: ... RESULT XML ... --------` ).
"This section covers syntax options after RESULT XML.
"Note: The following examples always use ABAP data objects as source
"(... SOURCE ...).
"ABAP -> asXML/xstring
"This syntax option has already been used above.
"When declared inline, the target data object has the type xstring.
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML DATA(xml_tab_e).
"Specifying a data object of type xstring
DATA xml_tab_f TYPE xstring.
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML xml_tab_f.
"ABAP -> asXML/string
DATA str TYPE string.
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML str.
"ABAP -> asXML/table with a flat character-like line type
TYPES c50 TYPE c LENGTH 50.
TYPES c50_tab_type TYPE TABLE OF c50 WITH EMPTY KEY.
DATA chartab TYPE c50_tab_type.
"The following statement inlcudes the OPTIONS addition.
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML chartab
OPTIONS xml_header = 'NO'.
out->write( data = chartab name = `chartab` ).
"ABAP -> asXML/table with a flat byte-like line type
TYPES x50 TYPE x LENGTH 50.
TYPES x50_tab_type TYPE TABLE OF x50 WITH EMPTY KEY.
DATA xtab TYPE x50_tab_type.
"The following statement inlcudes the OPTIONS addition
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML xtab
OPTIONS xml_header = 'NO'.
out->write( data = xtab name = `xtab` ).
"iXML/sXML references
"Using an iXML output stream
DATA xstrg TYPE xstring.
DATA(ixml) = cl_ixml_core=>create( ).
DATA(ixml_doc) = ixml->create_document( ).
DATA(ixml_cr_stream) = ixml->create_stream_factory( ).
DATA(ixml_o_stream) = ixml_cr_stream->create_ostream_xstring( string = xstrg ).
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML ixml_o_stream.
ixml->create_renderer( document = ixml_doc
ostream = ixml_o_stream )->render( ).
DATA(xml_from_ixml) = cl_abap_conv_codepage=>create_in( )->convert( xstrg ).
out->write( data = xml_from_ixml name = `xml_from_ixml` ).
"Using an sXML writer
DATA(sxml) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10
encoding = 'UTF-8' ) ).
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML sxml.
DATA(xml_from_sxml) = CAST cl_sxml_string_writer( sxml )->get_output( ).
DATA(conv_xml_from_sxml) = cl_abap_conv_codepage=>create_in( )->convert( xml_from_sxml ).
out->write( data = conv_xml_from_sxml name = `conv_xml_from_sxml` ).
**********************************************************************
out->write( `-------- Transforming to ABAP data: ... RESULT ... --------` ).
"This section covers syntax options after RESULT without XML.
"XML from above is used as source.
DATA tab_b LIKE tab.
CALL TRANSFORMATION id SOURCE XML xml_tab_a
RESULT itab = tab_b.
out->write( data = tab_b name = `tab_b` ).
"An internal table of type abap_trans_resbind_tab can be specified.
DATA tab_c LIKE tab.
DATA(restab) = VALUE abap_trans_resbind_tab( ( name = 'ITAB' value = REF #( tab_c ) ) ).
CALL TRANSFORMATION id SOURCE XML xml_tab_a
RESULT (restab).
out->write( data = tab_c name = `tab_c` ).
ENDMETHOD.
ENDCLASS.
- You can ..
- create/render and read/parse JSON data in ABAP using the readers and writers in the sXML library. See the processing of XML data in the sXML section above. Parsing and rendering JSON data works in a similar way. However, instead of using XML readers/writers, you use JSON readers/writers.
- transform ABAP to and from JSON data using transformations. You can directly transform ABAP <-> JSON using identity transformation (
ID
). In this context, note the intermediate format asJSON (see the notes on asXML above). - create and handle JSON data using the XCO library or
/ui2/cl_json
.
💡 Note
- In ABAP, the sXML library processes JSON data using JSON-XML, an SAP-specific JSON data representation in XML format. This intermediate step is used for both reading and creating JSON data. Find more information here.
- The following examples provide basic implementations to give you an idea. You should always work out your own solutions.
- Both token-based and object-oriented rendering/parsing methods are available. These examples use the token-based approach.
- For additional information and examples, see the ABAP and JSON section of the ABAP Keyword Documentation.
Expand the following collapsible section for example classes. To try them out, create a demo class named zcl_demo_abap
and paste the code into it. After activation, choose F9 in ADT to execute the class. This example is set up to display output in the console.
Creating JSON Data using the sXML library
🟢 Click to expand for example code
- The
create
method from thecl_sxml_string_writer
class is used to create a JSON writer by setting the type toif_sxml=>co_xt_json
. - Note the comments about the cast in the XML section.
- Several elements and attributes are created using token-based rendering methods.
- Finally, the created JSON data is displayed.
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
"In this example, the following JSON data should be created.
"[{"carrier_id":"LH","connection_id":"400","city_from":"Frankfurt","city_to":"Berlin"},
" {"carrier_id":"AZ","connection_id":"790","city_from":"Rome","city_to":"Osaka"}]
"Creating a JSON writer by specifying the type in the 'create' method appropriately
"The example uses token-based rendering.
DATA(json_writer) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ) ).
TRY.
json_writer->open_element( name = 'array' ).
json_writer->open_element( name = 'object' ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'carrier_id' ).
json_writer->write_value( value = 'LH' ).
json_writer->close_element( ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'connection_id' ).
json_writer->write_value( value = '400' ).
json_writer->close_element( ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'city_from' ).
json_writer->write_value( value = 'Frankfurt' ).
json_writer->close_element( ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'city_to' ).
json_writer->write_value( value = 'Berlin' ).
json_writer->close_element( ).
json_writer->close_element( ).
json_writer->open_element( name = 'object' ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'carrier_id' ).
json_writer->write_value( value = 'AZ' ).
json_writer->close_element( ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'connection_id' ).
json_writer->write_value( value = '790' ).
json_writer->close_element( ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'city_from' ).
json_writer->write_value( value = 'Rome' ).
json_writer->close_element( ).
json_writer->open_element( name = 'str' ).
json_writer->write_attribute( name = 'name' value = 'city_to' ).
json_writer->write_value( value = 'Osaka' ).
json_writer->close_element( ).
json_writer->close_element( ).
json_writer->close_element( ).
CATCH cx_sxml_state_error INTO DATA(error).
out->write( error->get_text( ) ).
ENDTRY.
DATA(json) = cl_abap_conv_codepage=>create_in( )->convert( CAST cl_sxml_string_writer( json_writer )->get_output( ) ).
out->write( json ).
ENDMETHOD.
ENDCLASS.
Reading JSON Data using the sXML library
🟢 Click to expand for example code
- An internal table is created for display purposes. This table is populated when iterating over all nodes. It includes information such as the node's value.
- For more details on the name component and others, refer to the ABAP Keyword Documentation.
- The
create
method from thecl_sxml_string_reader
class is used to create a JSON reader. - After iterating through all the nodes, the retrieved information that has been added to the internal table is displayed.
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
"Creating demo JSON data
DATA(json) = cl_abap_conv_codepage=>create_out( )->convert(
`[` &&
` {` &&
` "carrier_id": "LH",` &&
` "connection_id": "400",` &&
` "city_from": "Frankfurt",` &&
` "city_to": "Berlin"` &&
` },` &&
` {` &&
` "carrier_id": "DL",` &&
` "connection_id": "1984",` &&
` "city_from": "San Francisco",` &&
` "city_to": "New York"` &&
` },` &&
` {` &&
` "carrier_id": "AZ",` &&
` "connection_id": "790",` &&
` "city_from": "Rome",` &&
` "city_to": "Osaka"` &&
` }` &&
`]` ).
"Creating an internal table for display purposes
DATA: BEGIN OF node_info,
node_type TYPE string,
name TYPE string,
value TYPE string,
END OF node_info,
nodes_tab LIKE TABLE OF node_info.
"Creating reader
"In this example, no special methods are used. Therefore, a cast is not carried out.
DATA(reader) = cl_sxml_string_reader=>create( json ).
"To iterate accros all nodes, you can call the NEXT_NODE method.
TRY.
DO.
CLEAR node_info.
reader->next_node( ).
"When reaching the end of the XML data, the loop is exited.
IF reader->node_type = if_sxml_node=>co_nt_final.
EXIT.
ENDIF.
"You can access the properties of the node directly.
"For display purposes, the property information is stored in an internal table.
"The example here just uses simple demo JSON data. Not all properties are
"retrieved and displayed.
"Node type, see the interface if_sxml_node
node_info-node_type = SWITCH #( reader->node_type WHEN if_sxml_node=>co_nt_initial THEN `CO_NT_INITIAL`
WHEN if_sxml_node=>co_nt_element_open THEN `CO_NT_ELEMENT_OPEN`
WHEN if_sxml_node=>co_nt_element_close THEN `CO_NT_ELEMENT_CLOSE`
WHEN if_sxml_node=>co_nt_value THEN `CO_NT_VALUE`
WHEN if_sxml_node=>co_nt_attribute THEN `CO_NT_ATTRIBUTE`
ELSE `Error` ).
"Name of the element
node_info-name = reader->name.
"Character-like value (if it is textual data)
node_info-value = COND #( WHEN reader->node_type = if_sxml_node=>co_nt_value THEN reader->value ).
APPEND node_info TO nodes_tab.
"Once the method is called, you can directly access the attributes of the reader with the required
"properties of the node. When the parser is on the node of an element opening, you can use the method
"NEXT_ATTRIBUTE to iterate across the JSON attributes.
IF reader->node_type = if_sxml_node=>co_nt_element_open.
DO.
reader->next_attribute( ).
IF reader->node_type <> if_sxml_node=>co_nt_attribute.
EXIT.
ENDIF.
APPEND VALUE #( node_type = `CO_NT_ATTRIBUTE`
name = reader->name
value = reader->value ) TO nodes_tab.
ENDDO.
ENDIF.
ENDDO.
CATCH cx_sxml_state_error INTO DATA(error_parse_token).
out->write( error_parse_token->get_text( ) ).
ENDTRY.
out->write( nodes_tab ).
ENDMETHOD.
ENDCLASS.
The following code snippets show a selection of transformation options using the predefined identity transformation. Here, a JSON writer is specified as the target.
"ABAP (elementary type) -> JSON
DATA str_a TYPE string VALUE `Hello`.
"Creating a JSON writer (see the type specification; you can also specify other
"types, e.g. if_sxml=>co_xt_xml10 for an XML writer)
DATA(writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE hi = str_a
RESULT XML writer.
"Getting the output and converting to string
DATA(json) = cl_abap_conv_codepage=>create_in( )->convert( writer->get_output( ) ).
"json: {"HI":"Hello"}
"JSON -> ABAP
DATA str_b TYPE string.
"Note: CALL TRANSFORMATION handles JSON sources implicitly.
CALL TRANSFORMATION id SOURCE XML json
RESULT hi = str_b.
"str_b: Hello
"ABAP -> (un)formatted JSON
"In this example, an internal table is transformed to JSON.
TYPES: BEGIN OF demo_struc,
comp1 TYPE i,
comp2 TYPE string,
comp3 TYPE abap_boolean,
END OF demo_struc,
tab_type TYPE TABLE OF demo_struc WITH EMPTY KEY.
DATA(it) = VALUE tab_type( ( comp1 = 1 comp2 = `abc` comp3 = abap_true )
( comp1 = 2 comp2 = `def` comp3 = abap_false )
( comp1 = 3 comp2 = `ghi` comp3 = abap_true ) ).
DATA(json_wr) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE itab = it
RESULT XML json_wr.
DATA(json_output_xstr) = json_wr->get_output( ).
DATA(json_unformatted) = cl_abap_conv_codepage=>create_in( )->convert( json_output_xstr ).
*{"ITAB":[{"COMP1":1,"COMP2":"abc","COMP3":"X"},{"COMP1":2,"COMP2":"def","COMP3":""},{"COMP1":3,"COMP2":"ghi","COMP3":"X"}]}
"A cast is included for the writer (if_sxml_writer) to access special methods.
"See comments in the sXML section.
DATA(json_wr_formatting) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ) ).
"With the following method calls, the result is formatted.
json_wr_formatting->set_option( option = if_sxml_writer=>co_opt_linebreaks ).
json_wr_formatting->set_option( option = if_sxml_writer=>co_opt_indent ).
CALL TRANSFORMATION id SOURCE itab = it
RESULT XML json_wr_formatting.
DATA(json_formatted) = cl_abap_conv_codepage=>create_in( )->convert( CAST cl_sxml_string_writer( json_wr_formatting )->get_output( ) ).
*{
* "ITAB":
* [
* {
* "COMP1":1,
* "COMP2":"abc",
* "COMP3":"X"
* },
* {
* "COMP1":2,
* "COMP2":"def",
* "COMP3":""
* },
* {
* "COMP1":3,
* "COMP2":"ghi",
* "COMP3":"X"
* }
* ]
*}
Using the XCO library
The following snippets demonstrate creating and handling JSON data using the XCO library.
"Creating and populating a demo structure and internal table
DATA: BEGIN OF carrier_struc,
carrier_id TYPE c length 3,
connection_id TYPE n length 4,
city_from TYPE c length 20,
city_to TYPE c length 20,
END OF carrier_struc.
DATA carriers_tab LIKE TABLE OF carrier_struc WITH EMPTY KEY.
carrier_struc = VALUE #( carrier_id = 'AA' connection_id = '17' city_from = 'New York' city_to = 'San Francisco' ).
carriers_tab = VALUE #( ( carrier_id = 'AZ' connection_id = '788' city_from = 'Rome' city_to = 'Tokyo' )
( carrier_id = 'JL' connection_id = '408' city_from = 'Frankfurt' city_to = 'Tokyo' ) ).
"ABAP (structure) -> JSON using XCO
DATA(struc2json_xco) = xco_cp_json=>data->from_abap( carrier_struc )->to_string( ).
"Result: {"CARRIER_ID":"AA","CONNECTION_ID":"0017","CITY_FROM":"New York","CITY_TO":"San Francisco"}
"ABAP (internal table) -> JSON using XCO
DATA(itab2json_xco) = xco_cp_json=>data->from_abap( carriers_tab )->to_string( ).
"Result: [{"CARRIER_ID":"AZ","CONNECTION_ID":"0788","CITY_FROM":"Rome","CITY_TO":"Tokyo"},
" {"CARRIER_ID":"JL","CONNECTION_ID":"0408","CITY_FROM":"Frankfurt","CITY_TO":"Tokyo"}]
"JSON -> ABAP (structure) using XCO
DATA json2struc_xco LIKE carrier_struc.
xco_cp_json=>data->from_string( struc2json_xco )->write_to( REF #( json2struc_xco ) ).
"Result:
"CARRIER_ID CONNECTION_ID CITY_FROM CITY_TO
"AA 0017 New York San Francisco
"JSON -> ABAP (internal table) using XCO
DATA json2itab_xco LIKE carriers_tab.
xco_cp_json=>data->from_string( itab2json_xco )->write_to( REF #( json2itab_xco ) ).
"Result:
"CARRIER_ID CONNECTION_ID CITY_FROM CITY_TO
"AZ 0788 Rome Tokyo
"JL 0408 Frankfurt Tokyo
"Creating JSON using XCO
"You can check out more methods that offer various options to build
"the JSON by clicking CTRL + Space after '->' in ADT.
"In the following example, JSON data similar to above is created.
"First, a JSON data builder is created. Then, using different methods,
"JSON data is created.
DATA(json_builder_xco) = xco_cp_json=>data->builder( ).
json_builder_xco->begin_object(
)->add_member( 'CarrierId' )->add_string( 'DL'
)->add_member( 'ConnectionId' )->add_string( '1984'
)->add_member( 'CityFrom' )->add_string( 'San Francisco'
)->add_member( 'CityTo' )->add_string( 'New York'
)->end_object( ).
"Getting JSON data
DATA(json_created_xco) = json_builder_xco->get_data( )->to_string( ).
"Result: {"CarrierId":"DL","ConnectionId":"1984","CityFrom":"San Francisco","CityTo":"New York"}
"Transforming the created JSON to ABAP (structure)
"Note: The JSON was intentionally created without the underscores in the
"name to demonstrate the 'apply' method. The following example demonstrates
"a transformation of camel case and underscore notation. As above, check out
"more options by using CTRL + Space after '...transformation->'.
CLEAR json2struc_xco.
xco_cp_json=>data->from_string( json_created_xco )->apply( VALUE #(
( xco_cp_json=>transformation->pascal_case_to_underscore ) ) )->write_to( REF #( json2struc_xco ) ).
"Result
"CARRIER_ID CONNECTION_ID CITY_FROM CITY_TO
"DL 1984 San Francisco New York
Using the /ui2/cl_json Class
For more information, see the class documentation. Note that there are many additional and optional parameters available, some of which are explored in the example in the collapsible section below.
DATA(some_table) = VALUE string_table( ( `aaa` ) ( `bbb` ) ( `ccc` ) ).
"--------- ABAP -> JSON ---------
DATA(abap_to_json) = /ui2/cl_json=>serialize( data = some_table ).
"--------- JSON -> ABAP ---------
DATA json_to_abap_table TYPE string_table.
/ui2/cl_json=>deserialize( EXPORTING json = abap_to_json
CHANGING data = json_to_abap_table ).
Expand the following collapsible section for example code. To try it out, create a demo class named zcl_demo_abap
and paste the code into it. After activation, choose F9 in ADT to execute the class. This example is set up to display output in the console.
🟢 Click to expand for example code
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
TYPES: BEGIN OF demo_struc,
carrier_id TYPE c LENGTH 3,
connection_id TYPE n LENGTH 4,
city_from TYPE c LENGTH 20,
city_to TYPE c LENGTH 20,
END OF demo_struc.
DATA itab TYPE TABLE OF demo_struc WITH EMPTY KEY.
itab = VALUE #( ( carrier_id = 'AA' connection_id = '0017' city_from = 'New York' city_to = 'San Francisco' )
( carrier_id = 'AZ' connection_id = '0789' city_from = 'Tokyo' city_to = 'Rome' ) ).
"---------------- Serializing ----------------
DATA(abap_to_json) = /ui2/cl_json=>serialize( data = itab ).
"Note the many additional, optional parameters such as for formatting the
"serialized JSON. For more information, see the class documentation.
DATA(abap_to_json_pretty) = /ui2/cl_json=>serialize( data = itab
format_output = abap_true ).
DATA(abap_to_json_pretty_name) = /ui2/cl_json=>serialize( data = itab
format_output = abap_true
pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
out->write( `---------- ABAP -> JSON ----------` ).
out->write( abap_to_json ).
out->write( `---------- ABAP -> JSON (pretty printed) ----------` ).
out->write( abap_to_json_pretty ).
out->write( `---------- ABAP -> JSON (camel case) ----------` ).
out->write( abap_to_json_pretty_name ).
"---------------- Deserializing ----------------
DATA(json_to_abap) = abap_to_json.
DATA itab_json_to_abap LIKE itab.
/ui2/cl_json=>deserialize( EXPORTING json = json_to_abap
CHANGING data = itab_json_to_abap ).
out->write( `---------- JSON -> ABAP ----------` ).
out->write( itab_json_to_abap ).
"---------------- Deserializing: Applying name mapping ----------------
"Creating an internal table with different field names
TYPES: BEGIN OF demo_struc4map,
carr TYPE c LENGTH 3,
conn TYPE n LENGTH 4,
from TYPE c LENGTH 20,
to TYPE c LENGTH 20,
END OF demo_struc4map.
DATA itab4map TYPE TABLE OF demo_struc4map WITH EMPTY KEY.
/ui2/cl_json=>deserialize( EXPORTING json = json_to_abap
name_mappings = VALUE #( ( abap = 'CARR' json = `CARRIER_ID` )
( abap = 'CONN' json = `CONNECTION_ID` )
( abap = 'FROM' json = `CITY_FROM` )
( abap = 'TO' json = `CITY_TO` ) )
CHANGING data = itab4map ).
out->write( `---------- JSON -> ABAP (Name mapping) ----------` ).
out->write( itab4map ).
"---------------- Deserializing: Using JSON as xstring ----------------
DATA(json_xstring) = cl_abap_conv_codepage=>create_out( )->convert(
`[` &&
` {` &&
` "carrier_id": "LH",` &&
` "connection_id": "400",` &&
` "city_from": "Frankfurt",` &&
` "city_to": "Berlin"` &&
` },` &&
` {` &&
` "carrier_id": "DL",` &&
` "connection_id": "1984",` &&
` "city_from": "San Francisco",` &&
` "city_to": "New York"` &&
` },` &&
` {` &&
` "carrier_id": "AZ",` &&
` "connection_id": "790",` &&
` "city_from": "Rome",` &&
` "city_to": "Osaka"` &&
` }` &&
`]` ).
DATA itab_json_xstr_to_abap LIKE itab.
/ui2/cl_json=>deserialize( EXPORTING jsonx = json_xstring
CHANGING data = itab_json_xstr_to_abap ).
out->write( `---------- JSON (xstring) -> ABAP ----------` ).
out->write( itab_json_xstr_to_abap ).
"---------------- Deserializing: No equivalent ABAP type available ----------------
"The example assumes that there is no equivalent ABAP type available for JSON data
"that is to be deserialized. You can use the 'generate' method that has a
"returning parameter of the generic type 'ref to data'.
DATA(json) = `[{"CARRIER_ID":"AA","CONNECTION_ID":17,"CITY_FROM":"New York","CITY_TO":"San Francisco"},` &&
`{"CARRIER_ID":"AZ","CONNECTION_ID":789,"CITY_FROM":"Tokyo","CITY_TO":"Rome"}]`.
DATA(dref) = /ui2/cl_json=>generate( json = json ).
DATA(dref_xstr) = /ui2/cl_json=>generate( jsonx = json_xstring ).
"You can further process the content, for example, with RTTS as outlined in the
"Dynamic Programming cheat sheet.
IF dref IS BOUND.
out->write( `---------- JSON -> ABAP (unknown type) ----------` ).
out->write( dref->* ).
ENDIF.
IF dref_xstr IS BOUND.
out->write( `---------- JSON (xstring) -> ABAP (unknown type) ----------` ).
out->write( dref_xstr->* ).
ENDIF.
ENDMETHOD.
ENDCLASS.
- To serialize and deserialize objects (i.e. instances of classes), you can use
CALL TRANSFORMATION
statements. As a prerequisite, the classes must implement theIF_SERIALIZABLE_OBJECT
interface. The example uses the predefined identity transformationID
. - Find more information and examples here in the ABAP Keyword Documentation.
Expand the following collapsible section for example classes. To try them out, create a demo class named zcl_demo_abap
and paste the code into it. After activation, choose F9 in ADT to execute the class. The examples are set up to display output in the console.
🟢 Click to expand for example code
- When the class runs, it creates three instances, and three instance attributes are assigned values for each instance: the current UTC timestamp, a random number, and a UUID.
- These instances are then serialized and subsequently deserialized.
- The instance attributes are accessed, and their values are stored in an internal table and displayed.
Example 1:
- The example class implements the
IF_SERIALIZABLE_OBJECT
interface, using the standard behavior to serialize and deserialize all instance attributes (i.e. the helper methods mentioned below are not implemented). - The values of all deserialized instance attributes are displayed.
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: if_oo_adt_classrun,
if_serializable_object.
PROTECTED SECTION.
PRIVATE SECTION.
DATA timestamp TYPE utclong.
DATA random_number TYPE i.
DATA uuid TYPE sysuuid_x16.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA serialized_obj_tab TYPE string_table.
TYPES: BEGIN OF deserialized_obj_struc,
timestamp TYPE utclong,
random_number TYPE i,
uuid TYPE sysuuid_x16,
END OF deserialized_obj_struc.
DATA deserialized_obj_tab TYPE TABLE OF deserialized_obj_struc WITH EMPTY KEY.
"Creating objects, assigning values to instance attributes, and serializing objects
DO 3 TIMES.
DATA(oref) = NEW zcl_demo_abap( ).
oref->timestamp = utclong_current( ).
oref->random_number = cl_abap_random_int=>create( seed = cl_abap_random=>seed( )
min = 1
max = 100 )->get_next( ).
TRY.
oref->uuid = cl_system_uuid=>create_uuid_x16_static( ) .
CATCH cx_uuid_error.
ENDTRY.
DATA serialized_obj TYPE string.
CALL TRANSFORMATION id SOURCE obj = oref
RESULT XML serialized_obj.
APPEND serialized_obj TO serialized_obj_tab.
ENDDO.
"Deserializing objects
LOOP AT serialized_obj_tab INTO DATA(wa).
DATA deserialized_obj TYPE REF TO zcl_demo_abap.
CALL TRANSFORMATION id SOURCE XML wa
RESULT obj = deserialized_obj.
"Addressing instance attributes after deserialization
DATA(deserialized_timestamp) = deserialized_obj->timestamp.
DATA(deserialized_random_number) = deserialized_obj->random_number.
DATA(deserialized_uuid) = deserialized_obj->uuid.
"Adding deserialized instance attribute values to an internal table
APPEND VALUE #( timestamp = deserialized_timestamp
random_number = deserialized_random_number
uuid = deserialized_uuid
) TO deserialized_obj_tab.
ENDLOOP.
out->write( deserialized_obj_tab ).
ENDMETHOD.
ENDCLASS.
Example 2:
- The example class implements the
IF_SERIALIZABLE_OBJECT
interface, along with theSERIALIZE_HELPER
andDESERIALIZE_HELPER
methods. - These methods allow you to modify the standard behavior. Both methods must be implemented together, or not at all. If neither is implemented, the standard behavior applies.
- The methods must be declared as private instance methods. The
SERIALIZE_HELPER
method only has output parameters, whileDESERIALIZE_HELPER
only has input parameters. Any parameter specified for theSERIALIZE_HELPER
method must have an identically named and typed parameter in theDESERIALIZE_HELPER
method. - For example, you can implement these methods if you want to exclude certain attributes from (de)serialization.
- In the given example, one of the instance attributes is excluded from (de)serialization. As a result, the output only includes initial values for one of the attributes.
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: if_oo_adt_classrun,
if_serializable_object.
PROTECTED SECTION.
PRIVATE SECTION.
DATA timestamp TYPE utclong.
DATA random_number TYPE i.
DATA uuid TYPE sysuuid_x16.
METHODS:
serialize_helper EXPORTING timestamp TYPE utclong
random_number TYPE i,
deserialize_helper IMPORTING timestamp TYPE utclong
random_number TYPE i.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA serialized_obj_tab TYPE string_table.
TYPES: BEGIN OF deserialized_obj_struc,
timestamp TYPE utclong,
random_number TYPE i,
uuid TYPE sysuuid_x16,
END OF deserialized_obj_struc.
DATA deserialized_obj_tab TYPE TABLE OF deserialized_obj_struc WITH EMPTY KEY.
"Creating objects, assigning values to instance attributes, and serializing objects
DO 3 TIMES.
DATA(oref) = NEW zcl_demo_abap( ).
oref->timestamp = utclong_current( ).
oref->random_number = cl_abap_random_int=>create( seed = cl_abap_random=>seed( )
min = 1
max = 100 )->get_next( ).
TRY.
oref->uuid = cl_system_uuid=>create_uuid_x16_static( ) .
CATCH cx_uuid_error.
ENDTRY.
DATA serialized_obj TYPE string.
CALL TRANSFORMATION id SOURCE obj = oref
RESULT XML serialized_obj.
APPEND serialized_obj TO serialized_obj_tab.
ENDDO.
"Deserializing objects
LOOP AT serialized_obj_tab INTO DATA(wa).
DATA deserialized_obj TYPE REF TO zcl_demo_abap.
CALL TRANSFORMATION id SOURCE XML wa
RESULT obj = deserialized_obj.
"Addressing instance attributes after deserialization
DATA(deserialized_timestamp) = deserialized_obj->timestamp.
DATA(deserialized_random_number) = deserialized_obj->random_number.
DATA(deserialized_uuid) = deserialized_obj->uuid.
"Adding deserialized instance attribute values to an internal table
APPEND VALUE #( timestamp = deserialized_timestamp
random_number = deserialized_random_number
uuid = deserialized_uuid
) TO deserialized_obj_tab.
ENDLOOP.
out->write( deserialized_obj_tab ).
ENDMETHOD.
METHOD serialize_helper.
timestamp = me->timestamp.
random_number = me->random_number.
ENDMETHOD.
METHOD deserialize_helper.
me->timestamp = timestamp.
me->random_number = random_number.
ENDMETHOD.
ENDCLASS.
In the code snippets above and in the executable example, many operations are performed using binary data.
This excursion shows the conversion of string <-> xstring using a codepage. The examples use UTF-8.
For example, you can use the cl_abap_conv_codepage
class and the XCO library. Using the xco_cp
class of the XCO library, you can also process Base64 representations of raw binary data (see the snippet for xco_cp
in the String Processing section of the Regular ABAP Classes cheat sheet).
DATA(xml_string) = `<TXT>ABAP</TXT>`.
"string -> xstring
"Note: UTF-8 is used by default. Here, it is specified explicitly.
"Exception class that can be used: cx_sy_conversion_codepage
TRY.
DATA(conv_xstring) = cl_abap_conv_codepage=>create_out( codepage = `UTF-8` )->convert( xml_string ).
CATCH cx_sy_conversion_codepage.
ENDTRY.
"conv_xstring: 3C5458543E414241503C2F5458543E
"xstring -> string
DATA(conv_string) = cl_abap_conv_codepage=>create_in( )->convert( conv_xstring ).
"conv_string: <TXT>ABAP</TXT>
"As an alternative, you can use methods of the XCO library. More methods are available
"in this context. Check the options when choosing CTRL + Space after '->'
"string -> xstring
DATA(conv_xstring_xco) = xco_cp=>string( xml_string
)->as_xstring( xco_cp_character=>code_page->utf_8
)->value.
"xstring -> string
DATA(conv_string_xco) = xco_cp=>xstring( conv_xstring_xco
)->as_string( xco_cp_character=>code_page->utf_8
)->value.
You may want to process or store binary data. The data may be very large. You can compress the data in gzip format and decompress it for further processing using the cl_abap_gzip
class.
DATA(str) = `This is a data object of type string. It should be converted to xstring, compressed and decompressed.`.
DATA(xstr) = cl_abap_conv_codepage=>create_out( )->convert( str ).
DATA xstr_comp TYPE xstring.
"Compressing binary data
TRY.
cl_abap_gzip=>compress_binary( EXPORTING raw_in = xstr
IMPORTING gzip_out = xstr_comp ).
CATCH cx_parameter_invalid_range cx_sy_buffer_overflow cx_sy_compression_error.
ENDTRY.
"Comparing the length of the data objects
DATA(len_xstr) = xstrlen( xstr ). "101
DATA(len_xstr_comp) = xstrlen( xstr_comp ). "81
"Decompressing binary data
DATA xstr_decomp TYPE xstring.
TRY.
cl_abap_gzip=>decompress_binary( EXPORTING gzip_in = xstr_comp
IMPORTING raw_out = xstr_decomp ).
CATCH cx_parameter_invalid_range cx_sy_buffer_overflow cx_sy_compression_error.
ENDTRY.
DATA(len_xstr_decomp) = xstrlen( xstr_decomp ). "101
DATA(conv_str) = cl_abap_conv_codepage=>create_in( )->convert( xstr_decomp ).
"abap_true
DATA(is_equal) = xsdbool( len_xstr = len_xstr_decomp AND str = conv_str ).
- A data cluster groups data objects for temporary and persistent storage in a medium, such as an elementary data object of type
xstring
, or an internal table with a specific table type. - Potential uses include:
- Fast serialization and deserialization of data to and from
xstring
- Storing the data cluster (in a compressed form) in a DDIC database table field
- Passing data clusters through parameters in procedures for further evaluation, especially with large, complex data
- Fast serialization and deserialization of data to and from
- Related ABAP statements:
EXPORT
to write data objects to the memory mediumIMPORT
to read from the memory medium and extract the data objects
💡 Note
- Regarding data clusters, the focus in this section is on the fast serialization and deserialization of data to and from
xstring
.- Find more information here.
- More syntax options are available in Standard ABAP.
- Various exceptions can be raised when using these statements; see the related subtopics in the documentation.
- Additions to
IMPORT
statements offer conversion options.
The following example covers:
- Exporting a data cluster, storing it in a byte string, and importing it back to a data object
- Activating compression using the
COMPRESSION ON
addition - Specifying multiple data objects in the parameter list
- Alternative syntax for specifying parameters
- Dynamic specification of the parameter list
- Exporting and importing class objects
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
INTERFACES if_serializable_object.
METHODS constructor IMPORTING text TYPE string OPTIONAL.
PROTECTED SECTION.
PRIVATE SECTION.
DATA timestamp TYPE utclong.
DATA text TYPE string.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA buffer TYPE xstring.
"---- Exporting data cluster, storing it in byte string, -------
"---- and importing it to a data object again ------------------
"The following example exports an internal table to a byte string buffer, and
"imports the content to another internal table.
"The parameter list includes one data object. Compression is deactivated
"by default.
SELECT *
FROM zdemo_abap_fli
INTO TABLE @DATA(itab1).
EXPORT flights = itab1 TO DATA BUFFER buffer.
"Determining the length of the xstring (to compare it with the
"same example that activates compression).
DATA(strlen_export) = xstrlen( buffer ).
out->write( data = strlen_export name = `strlen_export` ).
out->write( |\n| ).
DATA itab2 LIKE itab1.
IMPORT flights = itab2 FROM DATA BUFFER buffer.
ASSERT itab2 = itab1.
out->write( data = itab2 name = `itab2` ).
out->write( |\n| ).
"---------------- Activating compression ----------------
EXPORT flights = itab1 TO DATA BUFFER buffer COMPRESSION ON.
DATA(strlen_export_compr) = xstrlen( buffer ).
ASSERT strlen_export_compr < strlen_export.
out->write( data = strlen_export_compr name = `strlen_export_compr` ).
out->write( |\n| ).
"------ Multiple data objects specified in the parameter list -----
"The following example writes and reads a byte string of two numbers.
DATA num1 TYPE i.
DATA num2 TYPE i.
EXPORT int1 = 100 int2 = 200 TO DATA BUFFER buffer.
IMPORT int1 = num1 int2 = num2 FROM DATA BUFFER buffer.
"----------------- Exceptions -----------------
"The following example includes a non-compatible data object to read
"to. The example emphasizes that various exceptions can be
"raised when using the statements. Find more details in the ABAP
"Keyword Documentation.
DATA str_table TYPE string_table.
TRY.
IMPORT int1 = num1 int2 = str_table FROM DATA BUFFER buffer.
CATCH cx_sy_import_mismatch_error.
ENDTRY.
"--------- Alternative syntax for specifying the parameters ---------
"EXPORT: Using ... FROM ... instead of ... = ...
"IMPORT: Using ... TO ... instead of ... = ...
EXPORT flights FROM itab1 TO DATA BUFFER buffer.
IMPORT flights TO itab2 FROM DATA BUFFER buffer.
EXPORT int1 = 100 int2 = 200 TO DATA BUFFER buffer.
IMPORT int1 TO num1 int2 TO num2 FROM DATA BUFFER buffer.
"--------- Dynamic specification of the parameter list ---------
"Note:
"- The parameter list is specified in an index table with two columns.
"- The column names can have random names, but the type must be character-like.
"- The first column represents the parameter name, the second column represents
" the name of the data object.
"- Note that special behavior applies. See the documentation.
TYPES: BEGIN OF param,
name TYPE string,
dobj TYPE string,
END OF param,
param_tab_type TYPE TABLE OF param WITH EMPTY KEY.
DATA: txt1 TYPE string VALUE `hello`,
txt2 TYPE string VALUE `world`,
txt3 TYPE string VALUE `ABAP`.
DATA(param_table) = VALUE param_tab_type(
( name = `txt1` dobj = `txt1` )
( name = `txt2` dobj = `txt2` )
( name = `txt3` dobj = `txt3` ) ).
EXPORT (param_table) TO DATA BUFFER buffer.
"The example reads the content into structure components.
DATA: BEGIN OF values,
txt1 TYPE string,
txt2 TYPE string,
txt3 TYPE string,
END OF values.
param_table = VALUE param_tab_type(
( name = `txt1` dobj = `values-txt1` )
( name = `txt2` dobj = `values-txt2` )
( name = `txt3` dobj = `values-txt3` ) ).
IMPORT (param_table) FROM DATA BUFFER buffer.
out->write( data = values name = `values` ).
out->write( |\n| ).
"--------- Exporting and importing objects of classes ----------
"The following example explores:
"- Objects of the example class are created
"- The objects are serialized using a CALL TRANSFORMATION statement
"- As a prerequisite, the class includes the IF_SERIALIZABLE_OBJECT interface
"- In a DO loop, 3 objects of the class are created.
"- After serializing the objects, the data is exported with compression activated.
"- Then, the data is read and deserialized again.
"- Instance attributes are accessed and added to an internal table for output
" purposes. The class is setup that instance attributes receive different values,
" for example, the current UTC timestamp is set.
TYPES: BEGIN OF info,
text TYPE string,
timestamp TYPE utclong,
END OF info.
DATA info_tab TYPE TABLE OF info WITH EMPTY KEY.
DO 3 TIMES.
DATA(obj_ref) = NEW zcl_demo_abap( |Instance { sy-index }| ).
CALL TRANSFORMATION id SOURCE oref = obj_ref RESULT XML DATA(xml).
EXPORT xml_data = xml TO DATA BUFFER buffer COMPRESSION ON.
CLEAR obj_ref.
IMPORT xml_data = xml FROM DATA BUFFER buffer.
CALL TRANSFORMATION id SOURCE XML xml RESULT oref = obj_ref.
DATA(text_deserialized) = obj_ref->text.
DATA(timestamp_deserialized) = obj_ref->timestamp.
APPEND VALUE #( text = text_deserialized timestamp = timestamp_deserialized ) TO info_tab.
ENDDO.
out->write( info_tab ).
out->write( |\n| ).
"--------- Exporting and importing data objects to an internal table as storage medium ----------
"The following example exports an internal table to a data cluster in an internal table,
"and imports it into another internal table.
"Note that there are prerequisites for the internal table as storage medium. See the documentation.
"As the width of the second column is restricted, the data is stored across multiple table lines.
TYPES: BEGIN OF buffer_line,
id TYPE i,
clstr TYPE x LENGTH 100,
END OF buffer_line.
DATA tab_buffer TYPE TABLE OF buffer_line WITH EMPTY KEY.
EXPORT itab = itab1 TO INTERNAL TABLE tab_buffer.
out->write( |Lines in buffer table: { lines( tab_buffer ) }| ).
out->write( |\n| ).
IMPORT itab = itab2 FROM INTERNAL TABLE tab_buffer.
ASSERT itab2 = itab1.
ENDMETHOD.
METHOD constructor.
me->timestamp = utclong_current( ).
IF text IS SUPPLIED AND text IS NOT INITIAL.
me->text = text.
ENDIF.
ENDMETHOD.
ENDCLASS.
- ABAP and XML (main topic in the ABAP Keyword Documentation)
- sXML
- iXML Library for ABAP Cloud
- XSLT
- SAP XSLT Processor Reference
- ST
- asXML
- asJSON
CALL TRANSFORMATION
💡 Note
- The executable example ...
- covers the following topics:
- Creating/Parsing XML Data Using iXML
- Creating/Parsing XML Data Using sXML
- XML Transformations using XSLT and Simple Transformations
- Serializations (ABAP -> XML) and Deserialization (XML -> ABAP) using the identity transformation ID (elementary types, structures, internal tables, data and object references)
CALL TRANSFORMATION
syntax options, sources and targets of transformations- Working with JSON data, XCO classes for JSON
- Excursions: Converting string <-> xstring, compressing and decompressing binary data
- uses, apart from the predefined identity transformation (ID), demo XSLT and ST programs. They are not intended to be role models for proper XSLT/ST design.
- The steps to import and run the code are outlined here.
- Disclaimer