Перейти до вмісту

XML та JSON

ABAP надає кілька механізмів для роботи з XML і JSON. Вибір залежить від задачі — від повного DOM-дерева до швидкої серіалізації внутрішньої таблиці.

ІнструментКоли застосовувати
iXML (cl_ixml_core)DOM-доступ: потрібно навігувати деревом, змінювати ноди, підтримувати DTD.
sXML (cl_sxml_string_writer/reader)Потоковий парсинг/рендеринг XML і JSON без повного DOM. Швидше і економніше.
CALL TRANSFORMATIONСеріалізація/десеріалізація ABAP ↔ XML/JSON через XSLT, Simple Transformation або тотожне перетворення ID.
XCO (xco_cp_json)Сучасний fluent-API для JSON у Cloud ABAP. Найпростіший варіант для типових задач.
/ui2/cl_jsonЗручний серіалізатор/десеріалізатор JSON у класичному ABAP, з name mapping і pretty-printing.

Бібліотека Integrated XML — повний DOM у пам’яті. Переваги: довільний доступ до нод, підтримка DTD. Мінус: високе споживання пам’яті на великих документах.

Створення XML:

DATA(ixml) = cl_ixml_core=>create( ).
DATA(document) = ixml->create_document( ).
" Корінь з namespace
DATA(root) = document->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->append_child( root ).
DATA(values) = document->create_element_ns( prefix = 'asx' name = 'values' ).
root->append_child( values ).
DATA(str_elem) = document->create_element_ns( name = 'STRING' ).
values->append_child( str_elem ).
str_elem->append_child( document->create_text( 'Hello World' ) ).
" Рендер у xstring
DATA xml_out TYPE xstring.
ixml->create_renderer(
document = document
ostream = ixml->create_stream_factory( )->create_ostream_xstring( string = xml_out )
)->render( ).

Парсинг XML:

DATA(xml_bin) = cl_abap_conv_codepage=>create_out( )->convert( `<root><a>hi</a></root>` ).
DATA(ixml) = cl_ixml_core=>create( ).
DATA(doc) = ixml->create_document( ).
DATA(parser) = ixml->create_parser(
istream = ixml->create_stream_factory( )->create_istream_xstring( string = xml_bin )
document = doc
stream_factory = ixml->create_stream_factory( ) ).
IF parser->parse( ) = 0.
" Прямий доступ за іменем
DATA(elem) = doc->find_from_name( name = `a` ).
DATA(val) = elem->get_value( ). " hi
" Ітератор по всіх нодах
DATA(iter) = doc->create_iterator( ).
DATA(node) = iter->get_next( ).
WHILE node IS NOT INITIAL.
CASE node->get_type( ).
WHEN if_ixml_node=>co_node_element.
" ... обробка елемента, атрибутів
WHEN if_ixml_node=>co_node_text.
" ... текстове значення
ENDCASE.
node = iter->get_next( ).
ENDWHILE.
ENDIF.

Бібліотека Serial XML — потокова обробка. Підтримує XML 1.0, XOP, binary XML, JSON. Без DOM — тому швидше й менше пам’яті. Два стилі: token-based (низькорівневий) та object-oriented (обгортка).

Рендеринг (token-based):

DATA(writer) = CAST if_sxml_writer(
cl_sxml_string_writer=>create( type = if_sxml=>co_xt_xml10 encoding = 'UTF-8' ) ).
TRY.
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 = 'number' ). writer->write_value( '400' ). writer->close_element( ).
writer->close_element( ).
writer->close_element( ).
CATCH cx_sxml_state_error.
ENDTRY.
DATA(xml) = CAST cl_sxml_string_writer( writer )->get_output( ). " xstring

Парсинг (token-based):

DATA(reader) = cl_sxml_string_reader=>create( xml_bin ).
TRY.
DO.
reader->next_node( ).
IF reader->node_type = if_sxml_node=>co_nt_final.
EXIT.
ENDIF.
CASE reader->node_type.
WHEN if_sxml_node=>co_nt_element_open.
" reader->name; ітерувати атрибути через next_attribute( )
WHEN if_sxml_node=>co_nt_value.
" reader->value
WHEN if_sxml_node=>co_nt_element_close.
" ...
ENDCASE.
ENDDO.
CATCH cx_sxml_state_error.
ENDTRY.

Трансформації працюють через проміжний формат asXML (для XML) або asJSON (для JSON). Варіанти:

ТипОпис
XSLTРепозиторний об’єкт із XSL-кодом.
Simple Transformation (ST)SAP-специфічна мова для ABAP ↔ XML.
Identity (ID)Вбудована тотожна трансформація. Серіалізує ABAP → asXML/asJSON і назад.
" Статичне ім'я трансформації
CALL TRANSFORMATION zsome_xslt SOURCE ... RESULT ...
" Тотожна трансформація ID
CALL TRANSFORMATION id SOURCE ... RESULT ...
" Динамічне ім'я
TRY.
CALL TRANSFORMATION ('ID') SOURCE ... RESULT ...
CATCH cx_invalid_transformation.
ENDTRY.
ФормаЗначення
SOURCE XML srcsrc містить XML 1.0 (string, xstring, таблиця, sXML/iXML reader)
SOURCE JSON srcJSON-дані
SOURCE name1 = abap1 name2 = abap2ABAP-об’єкти; nameN — ім’я XML-елемента
SOURCE (srctab)Динамічно, srctab типу abap_trans_srcbind_tab
ФормаЗначення
RESULT XML resXML у string/xstring/table/sXML writer/iXML ostream
RESULT JSON resJSON
RESULT name1 = abap1 ...ABAP-об’єкти
RESULT (restab)Динамічно, abap_trans_resbind_tab
TYPES: BEGIN OF line, char TYPE c LENGTH 3, int TYPE i, END OF line.
DATA(tab) = VALUE TABLE OF line WITH EMPTY KEY( ( char = 'aaa' int = 1 )
( char = 'bbb' int = 2 ) ).
" ABAP → asXML (xstring)
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML DATA(xml_out).
" XML → ABAP
DATA tab_back LIKE tab.
CALL TRANSFORMATION id SOURCE XML xml_out
RESULT itab = tab_back.
" OPTIONS для прибрання XML-заголовка
TYPES c50_tab TYPE TABLE OF c LENGTH 50 WITH EMPTY KEY.
DATA chartab TYPE c50_tab.
CALL TRANSFORMATION id SOURCE itab = tab
RESULT XML chartab
OPTIONS xml_header = 'NO'.

Той же cl_sxml_string_writer/reader, але з типом if_sxml=>co_xt_json. Всередині ABAP обробляє JSON через проміжний формат JSON-XML.

DATA(json_writer) = CAST if_sxml_writer(
cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ) ).
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->close_element( ).
DATA(json) = cl_abap_conv_codepage=>create_in( )->convert(
CAST cl_sxml_string_writer( json_writer )->get_output( ) ).
" {"carrier_id":"LH"}
" ABAP → JSON
DATA(writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE hi = `Hello`
RESULT XML writer.
DATA(json) = cl_abap_conv_codepage=>create_in( )->convert( writer->get_output( ) ).
" {"HI":"Hello"}
" JSON → ABAP через RESULT JSON / SOURCE JSON
DATA num TYPE i VALUE 123.
CALL TRANSFORMATION id SOURCE var = num
RESULT JSON DATA(json_out).
DATA int TYPE i.
CALL TRANSFORMATION id SOURCE JSON json_out
RESULT var = int.

Pretty-print — через опції writer’а:

DATA(w) = CAST if_sxml_writer( cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ) ).
w->set_option( option = if_sxml_writer=>co_opt_linebreaks ).
w->set_option( option = if_sxml_writer=>co_opt_indent ).
CALL TRANSFORMATION id SOURCE itab = it
RESULT XML w.

Найчистіший варіант для типових задач JSON.

DATA: BEGIN OF carrier,
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.
carrier = VALUE #( carrier_id = 'AA' connection_id = '17'
city_from = 'New York' city_to = 'San Francisco' ).
" ABAP → JSON
DATA(json) = xco_cp_json=>data->from_abap( carrier )->to_string( ).
" JSON → ABAP
DATA back LIKE carrier.
xco_cp_json=>data->from_string( json )->write_to( REF #( back ) ).
" Побудова вручну через builder
DATA(b) = xco_cp_json=>data->builder( ).
b->begin_object(
)->add_member( 'CarrierId' )->add_string( 'DL'
)->add_member( 'ConnectionId' )->add_string( '1984'
)->end_object( ).
DATA(result_json) = b->get_data( )->to_string( ).
" Name-конвенції (camelCase JSON → ABAP underscore)
xco_cp_json=>data->from_string( result_json
)->apply( VALUE #( ( xco_cp_json=>transformation->pascal_case_to_underscore ) )
)->write_to( REF #( back ) ).
DATA(tab) = VALUE string_table( ( `aaa` ) ( `bbb` ) ).
" Серіалізація з опціями
DATA(json) = /ui2/cl_json=>serialize( data = tab ).
DATA(json_pretty) = /ui2/cl_json=>serialize( data = tab
format_output = abap_true
pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
" Десеріалізація
DATA back TYPE string_table.
/ui2/cl_json=>deserialize( EXPORTING json = json
CHANGING data = back ).
" З name mapping (різні імена полів у JSON і ABAP)
/ui2/cl_json=>deserialize( EXPORTING json = some_json
name_mappings = VALUE #( ( abap = 'CARR' json = `CARRIER_ID` )
( abap = 'CONN' json = `CONNECTION_ID` ) )
CHANGING data = target ).
" Невідомий тип — згенерувати динамічно
DATA(dref) = /ui2/cl_json=>generate( json = some_json ). " ref to data

Серіалізація об’єктів

Section titled “Серіалізація об’єктів”

Об’єкти (instance класу) можна серіалізувати через CALL TRANSFORMATION id, якщо клас реалізує інтерфейс IF_SERIALIZABLE_OBJECT.

CLASS zcl_demo DEFINITION.
PUBLIC SECTION.
INTERFACES if_serializable_object.
PRIVATE SECTION.
DATA timestamp TYPE utclong.
DATA n TYPE i.
ENDCLASS.
" Серіалізувати
DATA(obj) = NEW zcl_demo( ).
obj->timestamp = utclong_current( ).
obj->n = 42.
DATA serialized TYPE string.
CALL TRANSFORMATION id SOURCE obj = obj
RESULT XML serialized.
" Десеріалізувати
DATA deserialized TYPE REF TO zcl_demo.
CALL TRANSFORMATION id SOURCE XML serialized
RESULT obj = deserialized.

Налаштування через SERIALIZE_HELPER / DESERIALIZE_HELPER: приватні instance-методи однакової сигнатури. Дозволяють виключити окремі атрибути з серіалізації. Обидва реалізуються або жодного.

Допоміжні перетворення

Section titled “Допоміжні перетворення”
" string → xstring (UTF-8 за замовчуванням)
DATA(bin) = cl_abap_conv_codepage=>create_out( codepage = `UTF-8` )->convert( `<TXT>ABAP</TXT>` ).
" xstring → string
DATA(str) = cl_abap_conv_codepage=>create_in( )->convert( bin ).
" Через XCO
DATA(bin2) = xco_cp=>string( `<TXT>ABAP</TXT>`
)->as_xstring( xco_cp_character=>code_page->utf_8 )->value.
DATA(str2) = xco_cp=>xstring( bin2
)->as_string( xco_cp_character=>code_page->utf_8 )->value.
DATA comp TYPE xstring.
DATA decomp TYPE xstring.
TRY.
cl_abap_gzip=>compress_binary( EXPORTING raw_in = raw_bin IMPORTING gzip_out = comp ).
cl_abap_gzip=>decompress_binary( EXPORTING gzip_in = comp IMPORTING raw_out = decomp ).
CATCH cx_parameter_invalid_range cx_sy_buffer_overflow cx_sy_compression_error.
ENDTRY.

Швидка бінарна серіалізація кількох об’єктів у xstring.

DATA buffer TYPE xstring.
" Запис (з компресією)
EXPORT flights = itab1 TO DATA BUFFER buffer COMPRESSION ON.
" Читання
DATA itab2 LIKE itab1.
IMPORT flights = itab2 FROM DATA BUFFER buffer.
" Кілька об'єктів одразу
EXPORT int1 = 100 int2 = 200 TO DATA BUFFER buffer.
IMPORT int1 = num1 int2 = num2 FROM DATA BUFFER buffer.

Коли використовувати що

Section titled “Коли використовувати що”
ЗадачаРішення
Навігація великим XML з DTDiXML
Потоковий парсинг XML/JSONsXML
ABAP-структура → XML/JSON і назад (типовий випадок)CALL TRANSFORMATION id + XCO
XML з нестандартним форматомXSLT або Simple Transformation
Швидке JSON ↔ ABAP у Cloudxco_cp_json
JSON з name mapping/ui2/cl_json
Серіалізація інстансу класуIF_SERIALIZABLE_OBJECT + CALL TRANSFORMATION id
Пакування кількох об’єктів у xstringEXPORT/IMPORT ... TO/FROM DATA BUFFER
Стиснути бінарні даніcl_abap_gzip
string ↔ xstringcl_abap_conv_codepage або XCO

Адаптовано з 21_XML_JSON.md (Apache 2.0).