Skip to main content

Writing JSON values

The methods of interface allows setting attributes, objects, arrays.

Individual value writing

data w type ref to zif_ajson.
w = zcl_ajson=>create_empty( ).

" Set value
" Results in { "a": { "b": { "num": 123, "str": "hello", "bool": true } } }
" The intermediary path is auto created, value type auto detected
w->set(
iv_path = '/a/b/num'
iv_val = 123 ).
w->set(
iv_path = '/a/b/str'
iv_val = 'hello' ).
w->set(
iv_path = '/a/b/bool'
iv_val = abap_true ).
w->set(
iv_path = '/a/b/str'
iv_val = 'escaping"\' ). " => "escaping\"\\", also with \n, \r, \t

" Ignoring empty values by default
w->set(
iv_path = '/a'
iv_val = abap_false ). " => nothing added to json !!!
w->set(
iv_ignore_empty = abap_false
iv_path = '/a'
iv_val = abap_false ). " => "a": false
w->set(
iv_path = '/a'
iv_val = 0 ). " => nothing added to json !!!
w->set(
iv_ignore_empty = abap_false
iv_path = '/a'
iv_val = 0 ). " => "a": 0

" With explicit type
w->set(
iv_path = '/a'
iv_val = '0'
iv_node_type = 'num' ). " => "a": 0

Individual TYPED values

" Set typed value
" IMPORTANTLY, empty values are always not ignored !
" Booleans -> converts not initial values to true
w->set_boolean(
iv_path = '/a'
iv_val = 123 ). " => true
w->set_boolean( " empty value not ignored !
iv_path = '/a'
iv_val = 0 ). " => false
w->set_boolean(
iv_path = '/a'
iv_val = 'abc' ). " => true
w->set_boolean(
iv_path = '/a'
iv_val = lt_non_empty_tab ). " => true

" Integer
w->set_integer( " this just forces conversion to int at param level
iv_path = '/a'
iv_val = 123 ). " => 123
w->set_integer( " empty value not ignored !
iv_path = '/a'
iv_val = 0 ). " => 0

" String (clike param)
w->set_string(
iv_path = '/a'
iv_val = sy-datum ). " => e.g. 20200705
w->set_string( " empty value not ignored !
iv_path = '/a'
iv_val = '' ). " => "a": ""

" Date - converts date param to json formatted date
w->set_date(
iv_path = '/a'
iv_val = sy-datum ). " => e.g. "2020-07-05" (with dashes)

" Timestamp - converts timestamp param to json formatted data as ISO (<YYYY>-<MM>-<DD>T<HH>:<MM>:<SS>Z)
get time stamp field lv_timestamp.
w->set_timestamp(
iv_path = '/a'
iv_val = lv_timestamp ). " => e.g. "2021-05-05T12-00-00Z" (with dashes)

" Null
" same effect is for initial data ref
w->set_null(
iv_path = '/a' ). " => "a": null

Text-based set

The method setx is a shortcut for full-scale set, it attempts to parse a string and detect both path and value from it. Although it is less performant (!) but it is more readable which can be beneficial for some cases where it is not critical e.g. setting constants in APIs or unit tests.
Format: path and value are separated by ':', space around path and around value is trimmed.

j->setx( '/a: 1' ).     " { "a": 1 }
j->setx( '/a: 1.123' ). " { "a": 1.123 }
j->setx( '/a: abc' ). " { "a": "abc" }
j->setx( '/a: "abc"' ). " { "a": "abc" }
j->setx( '/a: "123"' ). " { "a": "123" } - force string
j->setx( '/a: null' ). " { "a": null }
j->setx( '/a: true' ). " { "a": true }
j->setx( '/a: false' ). " { "a": false }

" deep path are supported
j->setx( '/a/b/c: 1' ).

" and also arrays and objects
" Note, the object must be in complete json format, with "",
" as such a value triggers full-scale parsing under the hood
j->setx( '/a: { "b": "abc" }' ).
j->setx( '/a: [1,2,3]' ).

" The method is chainable
j->setx( '/a: 1' )->setx( '/b: 2' ).

Deletion and overwriting

" Importantly, values and whole branches are rewritten
" { "a": { "b": 0 } } - the old "b" completely deleted
w->set(
iv_path = '/a/b'
iv_val = 0 ).

" Items can be deleted explicitly
w->delete( '/a/b' ). " => { "a": { } }

" Or completely cleared
w->clear( ).

Settings objects

" Set object
" Results in { "a": { "b": { "payload": { "text": ..., "num": ... } } } }
data:
begin of ls_payload,
text type string,
num type i,
end of ls_payload.
w->set(
iv_path = '/a/b/payload'
iv_val = ls_payload ).

" Set other object with ajson instance
w->set(
iv_path = '/a/b/payload'
iv_val = lo_another_ajson ).

Settings arrays/tables

" Set arrays
" Results in: { "array": [ "abc", "efg" ] }
" Tables of structures, of tables, and other deep objects are supported as well
data tab type string_table.
append 'abc' to tab.
append 'efg' to tab.
w->set(
iv_path = '/array'
iv_val = tab ).

" Fill arrays item by item
" Different types ? no problem
w->push(
iv_path = '/array'
iv_val = 1 ).
" => { "array": [ "abc", "efg", 1 ] }

w->push(
iv_path = '/array'
iv_val = ls_payload ).
" => { "array": [ "abc", "efg", 1, { "text": ..., "num": ... } ] }

" Push verifies that the path item exists and is array
" it does NOT auto create path like "set"
" to explicitly create an empty array use "touch_array"
w->touch_array( '/array2' ).
" => { "array": ..., "array2": [] }

Known issues

  • removing an array item in the middle of array will not renumber the items

Setting data refs

TBD

Currently not supported, but maybe in future. Except initial data ref which is equivalent to set_null.

  • set null
  • typed refs
  • any data refs
  • object refs

Chaining

Set (and some other) methods also return me to support chaining: li_json->set(...)->set(...)->touch_array(...)->push(...).

Freezing JSON (read only)

It is possible to set an instance of ajson immutable (read only). It is done on object level with method freeze or at parse time with iv_freeze = abap_true param. This is one way only change. After this set, delete, clear and other modification methods will raise exceptions if used. Useful to freeze some kind of settings or service responses.

Rendering to JSON string

zcl_ajson instance content can be rendered to JSON string using zif_ajson~stringify method (also has alias at class level). It also supports optional indentation.


data lo_json type ref to zcl_ajson.
data li_json type ref to zif_ajson.

lo_json = zcl_ajson=>create_empty( ).
li_json = lo_json.

li_json->set(
iv_path = '/a'
iv_val = 1 ).
li_json->set(
iv_path = '/b'
iv_val = 'B' ).
li_json->touch_array(
iv_path = '/e' ).
li_json->touch_array(
iv_path = '/f' ).
li_json->push(
iv_path = '/f'
iv_val = 5 ).

data lv type string.
lv = lo_json->stringify( ). " or li_json->stringify( ).
" {"a":1,"b":"B","e":[],"f":[5]}

lv = lo_json->stringify( iv_indent = 2 ). " indent with 2 spaces
" {
" "a": 1,
" "b": "B",
" "e": [],
" "f": [
" 5
" ]
" }

Keep item order

Sometimes you may want to keep order of JSON items in the same order as it was in ABAP structure (assuming you set structures or table of structures). To do this: set iv_keep_item_order flag when creating an instance or call keep_item_order after creation of instance, before any set.

  data:
begin of ls_dummy,
zulu type string,
alpha type string,
beta type string,
end of ls_dummy.

li_json->keep_item_order( ).
li_json->set(
iv_path = '/'
iv_val = ls_dummy ).
li_json->stringify( ). " '{"zulu":"z","alpha":"a","beta":"b"}'
" otherwise - '{"alpha":"a","beta":"b","zulu":"z"}'

" OR
li_json = zcl_ajson=>new( iv_keep_item_order = abap_true ).
...

The same parameter exists for parsing

  li_json = zcl_ajson=>parse( 
iv_json = '{"b":1,"a":2}'
iv_keep_item_order = abap_true ).
li_json->stringify( ). " '{"b":1,"a":2}'

Other

Checking current instance behavior options

Behavior options like read_only or keep_item_order are accessible via opts() method (returns zif_ajson=>ty_opts).