New: Z_UI2_JSON2 — kernel API edition available for SAP_BASIS 7.57+. Same public API, better performance, strict JSON compliance. See migration guide.
Class names: The SAP-delivered class is
/UI2/CL_JSON. The open-source Z* copy in this repository isZ_UI2_JSON. All code examples on this page use/UI2/CL_JSON— substituteZ_UI2_JSONif you are working with the local copy.
- Serialize classes, structures, internal tables, class and data references, and elementary types. Complex types, such as a table of structures/classes, classes with complex attributes, etc., are also supported and recursively processed.
- ABAP to JavaScript adopted a way of data type serializations:
- strings, character types to JavaScript string format (no length limitation),
- ABAP_BOOL / BOOLEAN / XFELD / BOOLE_D to JavaScript Boolean,
- Built-in TRIBOOL (TRUE/FALSE/UNDEFINED = 'X'/'-'/'') support, for better control of initial values when serializing into JavaScript Boolean
- int/floats/numeric/packed to JavaScript Integers/floats,
- date/time to JavaScript date/time string representation as "2015-03-24" or "15:30:48",
- timestamp to JavaScript integer or ISO8601 string
- structures to JavaScript objects (including types are also supported; aliases => AS are ignored)
- convert ABAP internal table to JSON, e.g JavaScript arrays or associative arrays (objects)
- Support of conversion exits on ABAP data serialization
- Pretty Printing of JavaScript property names: MY_DATA -> myData, /SAPAPO/MY_DATA -> sapapoMyData.
- Condensing of default values: initial values are not rendered into the resulting JSON string
- Optionally apply JSON formatting/beautifing/pretty-print for serialized JSON
- Performance is optimized for processing big internal tables with structures
- Deserialize JSON objects, arrays, and elementary types into corresponding ABAP structures. Complex objects, with embedded arrays and objects with any level of nesting, are also supported.
- Convert JSON to an internal table
- Generic deserialization of JSON objects into reference data types:
- as simple data types (integer, boolean, or string into a generic data reference (REF TO DATA) -> ABAP type is selected based on JSON type.
- as dynamically generated complex object (structures, tables, mixed) for initial REF TO DATA fields
- as typed references for prefilled REF TO DATA fields (you assign a reference to a typed empty data object to the REF TO DATA field in execution time)
- Deserialization of unknown JSON structures is possible using the method GENERATE into on-the-fly created data types
- On JSON to ABAP transformation following rules are used:
- objects parsed into corresponding ABAP structures, classes (only classes with constructors with no obligatory parameters are supported), or internal hash/sorted tables
- arrays converted to internal tables (complex tables are also supported).
- Boolean converted as ABAP_BOOL (‘’ or ‘X’)
- Date/Time/Timestamps from JSON converted based on the type of corresponding ABAP element
- integers/floats/strings moved to corresponding fields using ABAP move semantics (strings are un-escaped). There is no limit on the size of deserialized strings; the only restriction is the constraints of the receiving data type. Escaped Unicode symbols (\u001F) in strings are decoded.
- elementary data types are converted if they do not match: JavaScript integer can come into ABAP string or JavaScript string into ABAP integer, etc.
- Transformation considers property naming guidelines for JSON and ABAP so that camelCase names will be copied into the corresponding CAMEL_CASE field if the CAMELCASE field is not found in the ABAP structure. Please don't forget to use the same PRETTY_MODE for deserialization, as you have used for serialization.
- Default field values, specified in reference ABAP variable, are preserved, and not overwritten if not found in the JSON object
- Transformation of JSON structures into ABAP class instances is NOT supported.
- Support of conversion exits on deserialization
This online tool may help you to generate proper receiving ABAP structures for your input JSON.
The parser for serializing/deserializing uses single-pass parsing and is optimized to provide the best possible performance in ABAP in a release-independent way. But for time-critical applications, which have kernel version 7.20 and higher, it is recommended to use built-in JSON to ABAP transformations (CALL TRANSFORMATION).
CLASS demo DEFINITION.
PUBLIC SECTION.
CLASS-METHODS main.
ENDCLASS.
CLASS demo IMPLEMENTATION.
METHOD main.
DATA: lt_flight TYPE STANDARD TABLE OF sflight,
lrf_descr TYPE REF TO cl_abap_typedescr,
lv_json TYPE /ui2/cl_json=>json.
SELECT * FROM sflight INTO TABLE lt_flight.
" serialize table lt_flight into JSON, skipping initial fields and converting ABAP field names into camelCase
lv_json = /ui2/cl_json=>serialize( data = lt_flight
pretty_name = /ui2/cl_json=>pretty_mode-camel_case
compress = abap_true
).
cl_demo_output=>write_json( lv_json ).
CLEAR lt_flight.
"Deserialize JSON string json into internal table lt_flight, doing camelCase to ABAP-like field name mapping
/ui2/cl_json=>deserialize( EXPORTING json = lv_json pretty_name = /ui2/cl_json=>pretty_mode-camel_case
CHANGING data = lt_flight ).
" serialize ABAP object into JSON string
lrf_descr = cl_abap_typedescr=>describe_by_data( lt_flight ).
lv_json = /ui2/cl_json=>serialize( data = lrf_descr format_output = abap_true ).
cl_demo_output=>write_json( lv_json ).
cl_demo_output=>display( ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
demo=>main( ).[
{
"mandt": "120",
"carrid": "AA",
"connid": 17,
"fldate": "2018-08-15",
"price": 422.94,
"currency": "USD",
"planetype": "747-400",
"seatsmax": 385,
"seatsocc": 268,
"paymentsum": 192361.84
},
{
"mandt": "120",
"carrid": "AA",
"connid":17
},
...{
"ABSOLUTE_NAME":"\\TYPE=%_T00004S00000000O0000014656",
"DECIMALS":0,
"HAS_UNIQUE_KEY": false,
"INITIAL_SIZE":0,
"KEY":
[
{
"NAME": "MANDT"
},
{
"NAME": "CARRID"
},
{
"NAME": "CONNID"
}
....ABAP has no built-in boolean type. The class maps well-known types (ABAP_BOOL, ABAP_BOOLEAN, BOOLEAN, BOOLE_D, XFELD, XSDBOOLEAN, WDY_BOOLEAN) to JSON true/false. Unknown char-1 fields are serialized as strings.
The TRIBOOL/BOOLEAN type adds a three-state value: 'X' → true, '-' → false, '' → null.
TYPES:
BEGIN OF ts_flags,
active TYPE abap_bool, " X -> true, '' -> false
approved TYPE boolean, " X -> true, '-' -> false, '' -> null
END OF ts_flags.
DATA: ls_flags TYPE ts_flags,
lv_json TYPE /ui2/cl_json=>json.
ls_flags-active = abap_true.
ls_flags-approved = /ui2/cl_json=>c_tribool-false.
lv_json = /ui2/cl_json=>serialize( data = ls_flags ).
" -> {"ACTIVE":true,"APPROVED":false}
/ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = ls_flags ).By default, timestamps are serialized as ABAP numeric strings (e.g. 20160708123456). Pass TS_AS_ISO8601 = abap_true to produce ISO 8601 strings instead:
DATA: lv_ts TYPE timestamp VALUE '20160708123456',
lv_json TYPE /ui2/cl_json=>json.
" Default: numeric
lv_json = /ui2/cl_json=>serialize( data = lv_ts ).
" -> 20160708123456
" ISO 8601
lv_json = /ui2/cl_json=>serialize( data = lv_ts ts_as_iso8601 = abap_true ).
" -> "2016-07-08T12:34:56Z"
" Deserialize works with both formats regardless of TS_AS_ISO8601
/ui2/cl_json=>deserialize( EXPORTING json = lv_json CHANGING data = lv_ts ).Deserialization also accepts OData \/Date(milliseconds)\/ and PT10H34M55S (Edm.Time) formats.
NUMC fields are serialized as integers by default (leading zeros dropped). Set NUMC_AS_STRING = abap_true to preserve leading zeros:
DATA: lv_matnr TYPE matnr VALUE '000000000012345',
lv_json TYPE /ui2/cl_json=>json.
lv_json = /ui2/cl_json=>serialize( data = lv_matnr ).
" -> 12345
lv_json = /ui2/cl_json=>serialize( data = lv_matnr numc_as_string = abap_true ).
" -> "000000000012345"Deserialization handles both forms transparently.
When a sorted/hashed table has a multi-field unique key, ASSOC_ARRAYS = abap_true concatenates all key field values with - as separator to form the property name:
TYPES:
BEGIN OF ts_entry,
region TYPE char3,
lang TYPE lang,
label TYPE string,
END OF ts_entry.
DATA lt_entries TYPE HASHED TABLE OF ts_entry WITH UNIQUE KEY region lang.
" ... fill table ...
DATA(lv_json) = /ui2/cl_json=>serialize(
data = lt_entries
assoc_arrays = abap_true
pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
" -> {"DE-D":{"label":"Deutsch"},"DE-E":{"label":"German"},"US-E":{"label":"English"}}The separator is the constant MC_KEY_SEPARATOR (default -).
Two static methods are most interesting in common cases: SERIALIZE and DESERIALIZE. The rest of the public methods are defined as public only for reuse purposes if you want to build/extend your own serialization/deserialization code.
- > DATA (any) - any ABAP object/structure/table/element to be serialized
- > COMPRESS (bool, default = false) - tells serializer to skip empty elements/objects during serialization. So, all of which IS INITIAL = TRUE.
- > NAME (string, optional) - optional name of the serialized object. When provided, wraps the output as
"name": {...}instead of bare{...}. - > PRETTY_NAME (enum, optional)- mode controls how ABAP field names are transformed into JSON attribute names. More can be found in the description below.
- > TYPE_DESCR (ref to CL_ABAP_TYPEDESCR, optional) - if you already know the object type, pass it to improve performance.
- > ASSOC_ARRAYS (bool, default = false) - controls how to serialize hash or sorted tables with unique keys. More can be found in the description below.
- > ASSOC_ARRAYS_OPT (bool, default = false) - when set, the serializer will optimize the rendering of name-value associated arrays (hash maps) in JSON
- > TS_AS_ISO8601 (bool, default = false) - says serializer to output timestamps using ISO8601 format.
- > NUMC_AS_STRING (bool, default = false) - Controls how NUMC fields are serialized. If set to ABAP_TRUE, NUMC fields are serialized not as integers, but as strings, with all leading zeros. Deserialization works with both ways of NUMC serialized data.
- > EXPAND_INCLUDES (bool, default = true) - Controls how ABAP named includes (structures defined with
INCLUDE ... AS name) are serialized. When true (default), the fields of the include are serialized inline, as if they belonged directly to the parent structure. When false, the include is serialized as a nested JSON object under the include alias name. - > NAME_MAPPINGS (table) - ABAP<->JSON Name Mapping Table
- > CONVERSION_EXITS (bool, default = false) - use DDIC conversion exits on serialization of values (performance loss!)
- > FORMAT_OUTPUT (bool, default = false) - Indent, add formatting spaces, and split into lines serialized JSON. When enabled, output is indented with 2-space increments (Z_UI2_JSON) or the writer's default indent style (Z_UI2_JSON2 — format differs slightly but is semantically equivalent). Example:
{
"carrid": "AA",
"connid": 17,
"price": 422.94
}instead of {"carrid":"AA","connid":17,"price":422.94}.
- > HEX_AS_BASE64 (bool, default = true) - Serialize hex values as base64
- < R_JSON - output JSON string.
A simplified variant of SERIALIZE with fewer parameters. Useful for quick serialization without need for the full parameter set.
- > DATA (any) - any ABAP object to serialize
- > COMPRESS (bool, default = false) - skip initial/empty values
- > TYPE_DESCR (ref to CL_ABAP_TYPEDESCR, optional) - pre-known type descriptor for performance
- > PRETTY_NAME (enum, optional) - pretty-print mode
- > ASSOC_ARRAYS (bool, default = false) - serialize sorted/hashed tables as associative arrays
- > TS_AS_ISO8601 (bool, default = false) - output timestamps as ISO 8601 strings
- < R_JSON - output JSON string.
- > JSON (string, optional) - input JSON string to deserialize
- > JSONX (xstring, optional) - input JSON as raw bytes (alternative to JSON); use with JSONX_CP to specify encoding
- > JSONX_CP (string, default = 'UTF-8') - code page for JSONX input
- > PRETTY_NAME (enum, optional) - mode, controlling how JSON field names are mapped to ABAP component names. More can be found in the description below.
- > ASSOC_ARRAYS (bool, default = false) - controls how to deserialize JSON objects into hash or sorted tables with unique keys. More can be found in the description below.
- > ASSOC_ARRAYS_OPT (bool, default = false) - when set, the deserializer will take into account the optimized rendering of associated arrays (properties) in JSON.
- > NAME_MAPPINGS (table) - ABAP<->JSON Name Mapping Table
- > CONVERSION_EXITS (bool, default = false) - use DDIC conversion exits on deserialize of values (performance loss!)
- > HEX_AS_BASE64 (bool, default = true) - deserialize hex values as base64
- > GEN_OPTIMIZE (bool, default = false) - optimize generated types, structures, and table types for REF TO DATA
- <> DATA (any) - ABAP object/structure/table/element to be filled from JSON string. If the ABAP structure contains more fields than in the JSON object, the content of unmatched fields is preserved.
- > JSON (string) - input JSON object string to generate
- > JSONX (xstring) - input JSON object as a raw string to generate
- > PRETTY_NAME (enum, optional) - mode, controlling how JSON field names are mapped to ABAP component names. More can be found in the description below.
- > OPTIMIZE (bool, default = false) - optimize generated types, structures, and table types for REF TO DATA
- > NAME_MAPPINGS (table) - ABAP<->JSON Name Mapping Table
- < RR_DATA (REF TO DATA) - a reference to ABAP structure/table dynamically generated from JSON string.
In addition to the explained methods, two options need a wider explanation:
- NONE - ABAP component names are serialized as is (UPPERCASE).
- LOW_CASE - ABAP component names serialized in low case
- CAMEL_CASE - ABAP component types serialized in camelCase where the symbol "_" is treated as a word separator (and removed from the resulting name).
- PASCAL_CASE - ABAP component types serialized in PascalCase where the symbol "_" is treated as a word separator (and removed from the resulting name).
- EXTENDED - works the same way as CAMEL_CASE but also has extended logic for encoding special characters, such as: ".", "@", "~", etc. It shall be used if you need JSON names with characters not allowed for ABAP data component names. If you do not have special characters in JSON names, do not use it - the performance will be slower than CAMEL_CASE mode. Example: ABAP name '__A__SCHEMA' translates in JSON name '@schema'
Encoding rules (ABAP name → JSON name):
- '__E__' → '!'
- '__N__' → '#'
- '__D__' → '$'
- '__P__' → '%'
- '__M__' → '&'
- '__S__' → '*'
- '__H__' → '-'
- '__T__' → '~'
- '__L__' → '/'
- '__C__' → ':'
- '__V__' → '|'
- '__A__' → '@'
- '__O__' or '___' → '.' NONE and LOW_CASE work the same way for DESERIALIZE.
This option controls how hashed or sorted tables with unique keys are serialized/deserialized. Normally, ABAP internal tables are serialized into JSON arrays. Still, in some cases, you will like to serialize them as associative arrays (JSON objects) where every table row shall be reflected as a separate property of the JSON object. This can be achieved by setting the ASSOC_ARRAYS parameter to TRUE. If set, the serializer checks for sorted/hashed tables with a UNIQUE key(s) and serializes them as an object. The JSON property name, reflecting row, is constructed from values of fields, used in key separated by constant MC_KEY_SEPARATOR = '-'. If the table has only one field marked as key, the value of this single field becomes a property name and is REMOVED from the associated object (to eliminate redundancy). If TABLE_LINE is used as a unique key, all values of all fields construct key property names (separated by MC_KEY_SEPARATOR). During deserialization, logic works vice versa: if ASSOC_ARRAYS is set to TRUE, and the JSON object matches the internal hash or sorted table with the unique key, the object is transformed into the table, where every object property is reflected in a separate table row. If the ABAP table has only one key field, the property name is transformed into a value of this key field.
By default, when dumping hash/sorted tables with a unique key into JSON, the serializer will write the key field as the property name, and the rest of the fields will write the object value of properties:
TYPES: BEGIN OF ts_record,
key TYPE string,
value TYPE string,
END OF ts_record.
DATA: lt_act TYPE SORTED TABLE OF ts_record WITH UNIQUE KEY key.
lv_json = /ui2/cl_json=>serialize( data = lt_exp assoc_arrays = abap_true ).Output JSON
{
"KEY1": {
"value": "VALUE1"
},
"KEY2": {
"value": "VALUE2"
}
}But suppose you will use the assoc_arrays_opt flag during serialization. In that case, the serializer will try to omit unnecessary object nesting by dumping simple, name/value tables, containing only one key field and one value field:
lv_json = /ui2/cl_json=>serialize( data = lt_exp assoc_arrays = abap_true assoc_arrays_opt = abap_true ).Output JSON
{
"KEY1": "VALUE1",
"KEY2": "VALUE2"
}For deserialization, the flag tells the deserializer that the value shall be placed in a non-key field of the structure.
Here is an example of deserialization, in which the two options mentioned above can be used:
{
"id": "509e28db-6794-4e02-91d3-e35f4c7310e9",
"form": {
"Authorised": "TBB ENTERPRISE SDN. BHD.",
"Fax No:": "92225491",
"Issued Date :": "02-10-2024",
"Voluntary Excess": "0.00",
"Page": "2",
"Allianz General Insurance Company (Malaysia) Berhad": "(200601015674)",
}
}Look into sub-attributes of the "form" field. They can not be easily mapped into any ABAP structure, because attribute names contain spaces, special characters, etc. The best way would be to deserialize this fragment in a table with a key corresponding to the attribute name. The example code for deserialization will then look like this:
TYPES: BEGIN OF ts_name_value,
name TYPE string,
value TYPE string,
END OF ts_name_value,
BEGIN OF ts_data,
id TYPE string,
form TYPE SORTED TABLE OF ts_name_value WITH UNIQUE KEY name,
END OF ts_data.
DATA: ls_data TYPE ts_data.
/ui2/cl_json=>deserialize( EXPORTING json = lv_json
assoc_array = abap_true
assoc_array_opt = abap_true
CHANGING data = ls_data ). The minimum supported release is SAP_BASIS 7.31. The code has also been tested on 7.00–7.30, but these are not officially supported. If you plan to use it on SAP_BASIS 7.02 and higher (and do not need property name pretty-printing), better consider the standard solution for ABAP, using CALL TRANSFORMATION. It shall be faster when implemented in the kernel. Maybe the best will be, if you need support in lower SAP_BASIS releases as well as in 7.02 and higher, to modify the provided class in a way to generate the same JSON format as standard ABAP CALL TRANSFORMATION for JSON does and redirect flow to home-made code or built-in ABAP transformation depending on SAP_BASIS release.
- Be aware that usage of flag conversion_exits may significantly decrease performance - use only in cases when you are sure that you need it.
- Escaping property values can be expensive. To optimize performance, in this case, you can replace escapement code with some kernel-implemented function (from the cl_http_utility class, for example), instead of explicit REPLACE ALL OCCURRENCES calls.
- Unescaping can influence deserialization performance even worse, depending on the fact that your JSON has encoded \n\r\t\f\b\x. So, avoid their usage if you can.
- It is possible to significantly increase performance for serialization/deserialization by dropping the support of releases below 7.40. That can be realized by moving base parsing from ABAP to kernel-implemented classes cl_sxml_string_writer and cl_sxml_string_reader.
Due to optimization reasons, some methods were converted to macros to reduce overhead for calling methods for data type serialization. If performance in your case is not critical, and you prefer clean/debuggable code, you can replace macro calls with corresponding methods.