├── zcl_json.clas.macros.abap
├── package.devc.xml
├── .abapgit.xml
├── LICENSE
├── zcl_json.clas.xml
├── README.md
└── zcl_json.clas.abap
/zcl_json.clas.macros.abap:
--------------------------------------------------------------------------------
1 | *"* use this source file for any macro definitions you need
2 | *"* in the implementation part of the class
3 |
4 | define skip_to_next_character.
5 | while position < length and
6 | json+position(1) na '",:{}[]tfn0123456789.+-eE'.
7 | add 1 to position.
8 | endwhile.
9 | end-of-definition.
10 |
--------------------------------------------------------------------------------
/package.devc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | abap-json
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.abapgit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | E
6 | /
7 | PREFIX
8 |
9 | - /.gitignore
10 | - /LICENSE
11 | - /README.md
12 | - /package.json
13 | - /.travis.yml
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Guilherme Maeda
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/zcl_json.clas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ZCL_JSON
7 | E
8 | JSON
9 | 1
10 | X
11 | X
12 | X
13 |
14 |
15 |
16 | ZCL_JSON
17 | CALL_CONVERSION_EXIT
18 | E
19 | Call value conversion
20 |
21 |
22 | ZCL_JSON
23 | CLASS_CONSTRUCTOR
24 | E
25 | CLASS_CONSTRUCTOR
26 |
27 |
28 | ZCL_JSON
29 | CR
30 | E
31 | Carriage return
32 |
33 |
34 | ZCL_JSON
35 | DATE_ISO_TO_SAP
36 | E
37 | Convert an ISO-8601 date to SAP format
38 |
39 |
40 | ZCL_JSON
41 | DATE_SAP_TO_ISO
42 | E
43 | Convert an SAP date to ISO-8601 format
44 |
45 |
46 | ZCL_JSON
47 | DECODE
48 | E
49 | Decode a JSON string into a new data object
50 |
51 |
52 | ZCL_JSON
53 | DECODE_ANYTHING
54 | E
55 | Decode a generic data object
56 |
57 |
58 | ZCL_JSON
59 | DECODE_ARRAY
60 | E
61 | Decode a JSON array
62 |
63 |
64 | ZCL_JSON
65 | DECODE_FIELD
66 | E
67 | Decode a JSON field
68 |
69 |
70 | ZCL_JSON
71 | DECODE_KEYWORD
72 | E
73 | Decode a JSON keyword
74 |
75 |
76 | ZCL_JSON
77 | DECODE_NUMBER
78 | E
79 | Decode a JSON number
80 |
81 |
82 | ZCL_JSON
83 | DECODE_OBJECT
84 | E
85 | Decode a JSON object/structure
86 |
87 |
88 | ZCL_JSON
89 | DECODE_STRING
90 | E
91 | Decode a JSON string
92 |
93 |
94 | ZCL_JSON
95 | ENCODE
96 | E
97 | Encode a data object to JSON
98 |
99 |
100 | ZCL_JSON
101 | ENCODE_ANYTHING
102 | E
103 | Encode a generic data object toJSON
104 |
105 |
106 | ZCL_JSON
107 | ENCODE_FIELD
108 | E
109 | Encode a field to JSON
110 |
111 |
112 | ZCL_JSON
113 | ENCODE_OBJECT
114 | E
115 | Encode an ABAP object to JSON
116 |
117 |
118 | ZCL_JSON
119 | ENCODE_STRUCTURE
120 | E
121 | Encode a structure to JSON
122 |
123 |
124 | ZCL_JSON
125 | ENCODE_TABLE
126 | E
127 | Encode a table to JSON
128 |
129 |
130 | ZCL_JSON
131 | ESCAPE_STRING
132 | E
133 | Convert a value to an escaped string
134 |
135 |
136 | ZCL_JSON
137 | INCLUDE_EMPTY_VALUES
138 | E
139 | Include empty/initial elements
140 |
141 |
142 | ZCL_JSON
143 | LF
144 | E
145 | Line feed
146 |
147 |
148 | ZCL_JSON
149 | LOWERCASE_NAMES
150 | E
151 | Lowercase names
152 |
153 |
154 | ZCL_JSON
155 | PRETTY_PRINT
156 | E
157 | Return indented/readable JSON
158 |
159 |
160 | ZCL_JSON
161 | PRETTY_PRINT_JSON
162 | E
163 | Format a JSON string to indented format
164 |
165 |
166 | ZCL_JSON
167 | TIMESTAMP_ISO_TO_SAP
168 | E
169 | Convert an ISO-8601 timestamp to SAP format
170 |
171 |
172 | ZCL_JSON
173 | TIMESTAMP_SAP_TO_ISO
174 | E
175 | Convert an SAP timestamp to ISO-8601 format
176 |
177 |
178 | ZCL_JSON
179 | TIME_ISO_TO_SAP
180 | E
181 | Convert an ISO-8601 time to SAP format
182 |
183 |
184 | ZCL_JSON
185 | TIME_SAP_TO_ISO
186 | E
187 | Convert an SAP time to ISO-8601 format
188 |
189 |
190 | ZCL_JSON
191 | USE_CONVERSION_EXITS
192 | E
193 | Use ABAP field conversion
194 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## ABAP-JSON
2 |
3 | A JSON encoder/parser in ABAP.
4 |
5 |
6 | ### Features
7 |
8 | * Pure ABAP (ECC6 compatible)
9 | * Support for deep ABAP structures and tables (unlimited levels)
10 | * Output compact or pretty printed JSON
11 | * Output lower case or upper case field names
12 | * Correctly outputs/parses dates, times and timestamps
13 | * Uses conversion exits for input and output based on dynamic typing
14 |
15 |
16 | ### Installation
17 |
18 | Install the ZCL_JSON class as a global class.
19 |
20 |
21 | ### Usage
22 |
23 | #### Methods
24 |
25 |
26 |
27 | | encode |
28 |
29 | Import parameters:
30 | CLIKE name (optional) - Object name
31 | ANY value - ABAP value to be encoded
32 |
33 | Return: STRING - JSON string.
34 | |
35 |
36 | Encodes an ABAP variable to JSON (recursively).
37 |
38 | Simple variables will generate a JSON field (without enclosing brackets).
39 | Structures will generate a JSON object.
40 | Tables will generate a JSON array.
41 | |
42 |
43 |
44 | | decode |
45 |
46 | Import parameters:
47 | CLIKE json - JSON string
48 |
49 | Changing parameters:
50 | ANY value - ABAP value to be set with the JSON content.
51 | |
52 |
53 | Parses a JSON string into an ABAP variable (recursively).
54 |
55 | Simple JSON fields should be parsed into simple ABAP variables.
56 | JSON objects should be parsed into ABAP structures.
57 | JSON arrays should be parsed into ABAP tables.
58 |
59 | JSON fields that are not found in the ABAP variable will be ignored.
60 | JSON fields that are not compatible with the corresponding ABAP variable (eg. a JSON array matching an ABAP structure) will also be ignored.
61 | Conversion errors are not supported.
62 | |
63 |
64 |
65 |
66 | #### Properties
67 |
68 |
69 |
70 | | include_empty_values |
71 |
72 | When generating JSON, include ABAP fields that have no value.
73 | The statement IS INITIAL is used to determine if an ABAP field is empty.
74 | |
75 |
76 |
77 | | pretty_print |
78 |
79 | Generates human-friendly JSON, organized in lines and indented.
80 | This causes a serious performance impact, so it should only be used if the resulting JSON really needs to be read by a human.
81 | |
82 |
83 |
84 | | lowercase_names |
85 |
86 | When generating JSON, output field names in lower case. Field values are not affected by this.
87 | This is not applicable to JSON parsing, as ABAP variable names are not case sensitive.
88 | |
89 |
90 |
91 | | use_conversion_exits |
92 |
93 | When generating ou parsing JSON, use corresponding conversion exits for ABAP variables that have it defined in the ABAP dictionary.
94 | ABAP runtime type services (RTTS) is used to read Dictionary information for ABAP fields.
95 | ABAP variables must be correctly typed for this feature to work. Generically typed fields will not be converted.
96 | |
97 |
98 |
99 |
100 |
101 | ### Code Example
102 |
103 | ``` abap
104 | data: s_vendor type vmds_ei_extern,
105 | o_json type ref to zcl_json,
106 | v_json type string.
107 |
108 | cl_erp_vendor_api=>read_vendor(
109 | exporting iv_lifnr = '0004000000'
110 | importing es_vendor = s_vendor
111 | ).
112 |
113 | create object o_json.
114 | o_json->lowercase_names = abap_true.
115 | o_json->include_empty_values = abap_false.
116 | o_json->pretty_print = abap_true.
117 |
118 | v_json = o_json->encode( s_vendor ).
119 | ```
120 |
121 | Output:
122 | ```
123 | {
124 | "header": {
125 | "object_instance": {
126 | "lifnr": "4000000"
127 | },
128 | "object_task": "C"
129 | },
130 | "central_data": {
131 | "central": {
132 | "data": {
133 | "ktokk": "VV04",
134 | "adrnr": "71207"
135 | }
136 | },
137 | "address": {
138 | "postal": {
139 | "data": {
140 | "from_date": "0001-01-01",
141 | "to_date": "9999-12-31",
142 | "name": "BLACK HAT EVENTS REGISTRATION DEPT.",
143 | "city": "SAN FRAN",
144 | "district": "Suite 900 South Tower",
145 | "postl_cod1": "99999-9999",
146 | "street": "303 2ND STREET",
147 | "house_no": "SN",
148 | "country": "US",
149 | "countryiso": "US",
150 | "langu": "EN",
151 | "langu_iso": "EN",
152 | "region": "CA",
153 | "sort1": "BLACK HAT",
154 | "time_zone": "PST",
155 | "langu_cr": "PT",
156 | "langucriso": "PT"
157 | }
158 | },
159 | "remark": {
160 | "current_state": "X"
161 | },
162 | "communication": {
163 | "phone": {
164 | "current_state": "X"
165 | },
166 | "fax": {
167 | "current_state": "X"
168 | },
169 | "ttx": {
170 | "current_state": "X"
171 | },
172 | "tlx": {
173 | "current_state": "X"
174 | },
175 | "smtp": {
176 | "current_state": "X",
177 | "smtp": [
178 | {
179 | "contact": {
180 | "data": {
181 | "std_no": "X",
182 | "e_mail": "EMAIL@EMAIL",
183 | "email_srch": "EMAIL@EMAIL",
184 | "home_flag": "X",
185 | "consnumber": "001"
186 | }
187 | },
188 | "remark": {
189 | "current_state": "X"
190 | }
191 | }
192 | ]
193 | },
194 | "rml": {
195 | "current_state": "X"
196 | },
197 | "x400": {
198 | "current_state": "X"
199 | },
200 | "rfc": {
201 | "current_state": "X"
202 | },
203 | "prt": {
204 | "current_state": "X"
205 | },
206 | "ssf": {
207 | "current_state": "X"
208 | },
209 | "uri": {
210 | "current_state": "X"
211 | },
212 | "pager": {
213 | "current_state": "X"
214 | }
215 | },
216 | "version": {
217 | "current_state": "X"
218 | }
219 | },
220 | "text": {
221 | "current_state": "X"
222 | },
223 | "vat_number": {
224 | "current_state": "X"
225 | },
226 | "tax_grouping": {
227 | "current_state": "X"
228 | },
229 | "bankdetail": {
230 | "current_state": "X"
231 | },
232 | "subrange": {
233 | "current_state": "X"
234 | }
235 | },
236 | "company_data": {
237 | "current_state": "X",
238 | "company": [
239 | {
240 | "data_key": {
241 | "bukrs": "21"
242 | },
243 | "data": {
244 | "akont": "21011001",
245 | "zwels": "BCEGMOPRTU",
246 | "zterm": "D007",
247 | "fdgrv": "V3",
248 | "reprf": "X"
249 | },
250 | "dunning": {
251 | "current_state": "X"
252 | },
253 | "wtax_type": {
254 | "current_state": "X"
255 | },
256 | "texts": {
257 | "current_state": "X"
258 | }
259 | },
260 | {
261 | "data_key": {
262 | "bukrs": "29"
263 | },
264 | "data": {
265 | "akont": "21011001",
266 | "zwels": "BCEGMOPRTU",
267 | "zterm": "D007",
268 | "fdgrv": "V3",
269 | "reprf": "X"
270 | },
271 | "dunning": {
272 | "current_state": "X"
273 | },
274 | "wtax_type": {
275 | "current_state": "X"
276 | },
277 | "texts": {
278 | "current_state": "X"
279 | }
280 | }
281 | ]
282 | },
283 | "purchasing_data": {
284 | "current_state": "X",
285 | "purchasing": [
286 | {
287 | "data_key": {
288 | "ekorg": "VV01"
289 | },
290 | "data": {
291 | "waers": "USD",
292 | "zterm": "D007"
293 | },
294 | "functions": {
295 | "current_state": "X"
296 | },
297 | "texts": {
298 | "current_state": "X"
299 | },
300 | "purchasing2": {
301 | "current_state": "X"
302 | }
303 | }
304 | ]
305 | }
306 | }
307 | ```
308 |
309 |
310 | ### Contributors
311 |
312 | * Guilherme Maeda (http://abap.ninja)
313 |
314 |
315 | ### License
316 |
317 | This code is distributed under the MIT License, meaning you can freely and unrestrictedly use it, change it, share it, distribute it and package it with your own programs as long as you keep the copyright notice, license and disclaimer.
318 |
--------------------------------------------------------------------------------
/zcl_json.clas.abap:
--------------------------------------------------------------------------------
1 | class ZCL_JSON definition
2 | public
3 | final
4 | create public .
5 |
6 | public section.
7 |
8 | data INCLUDE_EMPTY_VALUES type FLAG value 'X' ##NO_TEXT.
9 | data PRETTY_PRINT type FLAG .
10 | data LOWERCASE_NAMES type FLAG value 'X' ##NO_TEXT.
11 | data USE_CONVERSION_EXITS type FLAG value 'X' ##NO_TEXT.
12 |
13 | class-methods CLASS_CONSTRUCTOR .
14 | methods ENCODE
15 | importing
16 | value(NAME) type CLIKE optional
17 | value(VALUE) type ANY
18 | returning
19 | value(JSON) type STRING .
20 | methods DECODE
21 | importing
22 | value(JSON) type CLIKE
23 | changing
24 | value(VALUE) type ANY .
25 | protected section.
26 | private section.
27 |
28 | class-data CR type C .
29 | class-data LF type C .
30 |
31 | class-methods PRETTY_PRINT_JSON
32 | importing
33 | value(JSON_IN) type CLIKE
34 | returning
35 | value(JSON_OUT) type STRING .
36 | class-methods DATE_SAP_TO_ISO
37 | importing
38 | value(DATE) type DATS
39 | returning
40 | value(RESULT) type STRING .
41 | class-methods DATE_ISO_TO_SAP
42 | importing
43 | value(DATE) type CLIKE
44 | returning
45 | value(RESULT) type DATS .
46 | class-methods TIME_SAP_TO_ISO
47 | importing
48 | value(TIME) type TIMS
49 | returning
50 | value(RESULT) type STRING .
51 | class-methods TIME_ISO_TO_SAP
52 | importing
53 | value(TIME) type CLIKE
54 | returning
55 | value(RESULT) type TIMS .
56 | class-methods TIMESTAMP_SAP_TO_ISO
57 | importing
58 | value(TIMESTAMP) type TIMESTAMP
59 | returning
60 | value(RESULT) type STRING .
61 | class-methods TIMESTAMP_ISO_TO_SAP
62 | importing
63 | value(TIMESTAMP) type CLIKE
64 | returning
65 | value(RESULT) type TIMESTAMP .
66 | class-methods ESCAPE_STRING
67 | importing
68 | value(INPUT) type CLIKE
69 | returning
70 | value(RESULT) type STRING .
71 | methods ENCODE_ANYTHING
72 | importing
73 | value(NAME) type CLIKE optional
74 | value(VALUE) type ANY
75 | returning
76 | value(JSON) type STRING .
77 | methods ENCODE_OBJECT
78 | importing
79 | value(NAME) type CLIKE optional
80 | !TYPE type ref to CL_ABAP_TYPEDESCR
81 | value(VALUE) type ANY
82 | returning
83 | value(JSON) type STRING .
84 | methods ENCODE_STRUCTURE
85 | importing
86 | value(NAME) type CLIKE optional
87 | !TYPE type ref to CL_ABAP_TYPEDESCR
88 | value(VALUE) type ANY
89 | returning
90 | value(JSON) type STRING .
91 | methods ENCODE_TABLE
92 | importing
93 | value(NAME) type CLIKE optional
94 | !TYPE type ref to CL_ABAP_TYPEDESCR
95 | value(VALUE) type ANY
96 | returning
97 | value(JSON) type STRING .
98 | methods ENCODE_FIELD
99 | importing
100 | value(NAME) type CLIKE
101 | !TYPE type ref to CL_ABAP_TYPEDESCR
102 | value(VALUE) type ANY
103 | returning
104 | value(JSON) type STRING .
105 | methods DECODE_ANYTHING
106 | importing
107 | !JSON type STRING
108 | !LENGTH type INT4
109 | changing
110 | !POSITION type INT4
111 | !VALUE type ANY .
112 | methods DECODE_OBJECT
113 | importing
114 | !NAME type CLIKE
115 | !JSON type STRING
116 | !LENGTH type INT4
117 | changing
118 | !POSITION type INT4
119 | !VALUE type ANY .
120 | methods DECODE_ARRAY
121 | importing
122 | !NAME type CLIKE
123 | !JSON type STRING
124 | !LENGTH type INT4
125 | changing
126 | !POSITION type INT4
127 | !VALUE type ANY .
128 | methods DECODE_FIELD
129 | importing
130 | !NAME type CLIKE
131 | !JSON type CLIKE
132 | !LENGTH type INT4
133 | changing
134 | !VALUE type ANY
135 | !POSITION type INT4 .
136 | methods DECODE_STRING
137 | importing
138 | !JSON type CLIKE
139 | !LENGTH type INT4
140 | changing
141 | !POSITION type INT4
142 | value(STRING) type STRING .
143 | methods DECODE_KEYWORD
144 | importing
145 | !JSON type CLIKE
146 | !LENGTH type INT4
147 | changing
148 | !POSITION type INT4
149 | value(STRING) type STRING .
150 | methods DECODE_NUMBER
151 | importing
152 | !JSON type CLIKE
153 | !LENGTH type INT4
154 | changing
155 | !POSITION type INT4
156 | value(STRING) type STRING .
157 | methods CALL_CONVERSION_EXIT
158 | importing
159 | value(DIRECTION) type DIRECTION
160 | value(TYPE) type ref to CL_ABAP_TYPEDESCR
161 | value(VALUE) type ANY
162 | changing
163 | value(RESULT) type ANY .
164 | ENDCLASS.
165 |
166 |
167 |
168 | CLASS ZCL_JSON IMPLEMENTATION.
169 |
170 |
171 | method call_conversion_exit.
172 | data: ddic_objects type dd_x031l_table,
173 | function_name type string,
174 | cstr_value type c length 255.
175 | field-symbols: like line of ddic_objects.
176 |
177 | "// Call conversion exit function
178 | type->get_ddic_object(
179 | receiving p_object = ddic_objects
180 | exceptions others = 8
181 | ).
182 | read table ddic_objects index 1 assigning .
183 | if sy-subrc = 0 and -convexit is not initial.
184 | cstr_value = value.
185 | if direction = 1.
186 | concatenate 'CONVERSION_EXIT_' -convexit '_OUTPUT'
187 | into function_name.
188 | else.
189 | concatenate 'CONVERSION_EXIT_' -convexit '_INPUT'
190 | into function_name.
191 | endif.
192 | try.
193 | call function function_name
194 | exporting
195 | input = cstr_value
196 | importing
197 | output = result
198 | exceptions
199 | others = 8.
200 | catch cx_root.
201 | endtry.
202 | endif.
203 | endmethod.
204 |
205 |
206 | method CLASS_CONSTRUCTOR.
207 | "// Initialize static attributes
208 | field-symbols type x.
209 |
210 | assign cr to casting. = 13.
211 | assign lf to casting. = 10.
212 | endmethod.
213 |
214 |
215 | method DATE_ISO_TO_SAP.
216 | data: year(4) type n,
217 | month(2) type n,
218 | day(2) type n.
219 |
220 | "// ISO-8601 allowed formats:
221 | "// YYYY-MM-DD or YYYYMMDD or YYYY-MM
222 | find regex '(\d{4})-?(\d{2})-?(\d{2})?' in date
223 | submatches year month day.
224 | if year is initial and
225 | month is initial and
226 | day is initial.
227 | return.
228 | endif.
229 | if day is initial.
230 | day = 1.
231 | endif.
232 |
233 | concatenate year month day into result.
234 | endmethod.
235 |
236 |
237 | method DATE_SAP_TO_ISO.
238 | concatenate date(4) '-' date+4(2) '-' date+6(2) into result.
239 | endmethod.
240 |
241 |
242 | method DECODE.
243 | data: decode_string type string,
244 | decode_length type i,
245 | decode_pos type i.
246 |
247 | "// Prepare decoding
248 | decode_string = json.
249 | condense decode_string.
250 | decode_length = strlen( decode_string ).
251 | decode_pos = 0.
252 |
253 | clear value.
254 |
255 | "// Decode JSON string into value object
256 | decode_anything(
257 | exporting
258 | json = decode_string
259 | length = decode_length
260 | changing
261 | position = decode_pos
262 | value = value
263 | ).
264 | endmethod.
265 |
266 |
267 | method DECODE_ANYTHING.
268 | data: name type string,
269 | string_value type string,
270 | has_name type flag.
271 |
272 | "// Skip padding characters
273 | skip_to_next_character.
274 | check position < length.
275 |
276 | "// Member has a name?
277 | if json+position(1) = '"'.
278 | has_name = 'X'.
279 | decode_string(
280 | exporting
281 | json = json
282 | length = length
283 | changing
284 | position = position
285 | string = name
286 | ).
287 | endif.
288 |
289 | "// Skip padding characters
290 | skip_to_next_character.
291 | check position < length.
292 |
293 | "// Check if this is a single value or an attribute
294 | if has_name = 'X'.
295 | case json+position(1).
296 | when ','.
297 | value = name.
298 | add 1 to position.
299 | return.
300 | when ':'.
301 | add 1 to position.
302 | when ']' or '}'.
303 | value = name.
304 | return.
305 | endcase.
306 | endif.
307 |
308 | "// Skip padding characters
309 | skip_to_next_character.
310 | check position < length.
311 |
312 | "// Decode member value
313 | case json+position(1).
314 | when '"'. "// begin string
315 | decode_field(
316 | exporting
317 | name = name
318 | json = json
319 | length = length
320 | changing
321 | position = position
322 | value = value
323 | ).
324 |
325 | when '{'.
326 | "// begin object => structure
327 | decode_object(
328 | exporting
329 | name = name
330 | json = json
331 | length = length
332 | changing
333 | position = position
334 | value = value
335 | ).
336 |
337 | when '['.
338 | "// begin array => table
339 | decode_array(
340 | exporting
341 | name = name
342 | json = json
343 | length = length
344 | changing
345 | position = position
346 | value = value
347 | ).
348 |
349 | when others.
350 | "// begin keyword or number
351 | decode_field(
352 | exporting
353 | name = name
354 | json = json
355 | length = length
356 | changing
357 | position = position
358 | value = value
359 | ).
360 |
361 | endcase.
362 |
363 | "// Check if object/array has just ended
364 | skip_to_next_character.
365 | check position < length.
366 |
367 | if json+position(1) na ']}'.
368 | add 1 to position.
369 | endif.
370 | endmethod.
371 |
372 |
373 | method DECODE_ARRAY.
374 | data: abap_name type string,
375 | typekind type c,
376 | dummy type table of syst,
377 | ld_line type ref to data.
378 |
379 | field-symbols: type any table,
380 | type any.
381 |
382 | abap_name = name. translate abap_name to upper case.
383 | describe field value type typekind.
384 | if typekind = cl_abap_typedescr=>typekind_table.
385 | assign value to .
386 | else.
387 | assign component abap_name of structure value to .
388 | if sy-subrc <> 0.
389 | assign dummy to .
390 | endif.
391 | endif.
392 |
393 | add 1 to position.
394 |
395 | "// Decode member value
396 | while position < length and
397 | json+position(1) <> ']'.
398 |
399 | create data ld_line like line of .
400 | assign ld_line->* to .
401 |
402 | decode_anything(
403 | exporting
404 | json = json
405 | length = length
406 | changing
407 | position = position
408 | value =
409 | ).
410 |
411 | insert into table .
412 |
413 | "// Skip padding characters
414 | skip_to_next_character.
415 | check position < length.
416 | endwhile.
417 | add 1 to position.
418 | endmethod.
419 |
420 |
421 | method decode_field.
422 | data: abap_name type string,
423 | str_value type string,
424 | type type ref to cl_abap_typedescr,
425 | relative_name type string.
426 | field-symbols: type any.
427 |
428 | "// Skip padding characters
429 | skip_to_next_character.
430 | check position < length.
431 |
432 | "// Decode field value
433 | case json+position(1).
434 | when '"'. "// String
435 | decode_string(
436 | exporting
437 | json = json
438 | length = length
439 | changing
440 | position = position
441 | string = str_value
442 | ).
443 | when 't' or 'f' or 'n'. "// Keyword
444 | decode_keyword(
445 | exporting
446 | json = json
447 | length = length
448 | changing
449 | position = position
450 | string = str_value
451 | ).
452 | when others. "// Numbers
453 | decode_number(
454 | exporting
455 | json = json
456 | length = length
457 | changing
458 | position = position
459 | string = str_value
460 | ).
461 | endcase.
462 |
463 | abap_name = name. translate abap_name to upper case.
464 | assign component abap_name of structure value to .
465 | if sy-subrc <> 0.
466 | return.
467 | endif.
468 |
469 | "// Get type kind
470 | type ?= cl_abap_typedescr=>describe_by_data( ).
471 |
472 | "// Timestamp? (becomes ISO-8601)
473 | relative_name = type->get_relative_name( ).
474 | if relative_name = 'TIMESTAMP'.
475 | = timestamp_iso_to_sap( str_value ).
476 | else.
477 | case type->type_kind.
478 | "// Date fields (become ISO-8601)
479 | when cl_abap_typedescr=>typekind_date.
480 | = date_iso_to_sap( str_value ).
481 |
482 | "// Time fields (become ISO-8601)
483 | when cl_abap_typedescr=>typekind_time.
484 | = time_iso_to_sap( str_value ).
485 |
486 | "// Anything else gets the default SAP input conversion
487 | when others.
488 | = str_value.
489 | if me->use_conversion_exits is not initial.
490 | call_conversion_exit(
491 | exporting direction = 2
492 | type = type
493 | value = str_value
494 | changing result =
495 | ).
496 | endif.
497 | endcase.
498 | endif.
499 | endmethod.
500 |
501 |
502 | method decode_keyword.
503 | data: first_char type c.
504 |
505 | first_char = json+position(1).
506 | while position < length.
507 | if json+position(1) na 'truefalsn'. "// true, false and null
508 | exit.
509 | endif.
510 | add 1 to position.
511 | endwhile.
512 |
513 | case first_char.
514 | when 't'. "// true
515 | string = 'X'.
516 | when 'f'. "// false
517 | string = space.
518 | when 'n'. "// null
519 | string = ''.
520 | endcase.
521 | endmethod.
522 |
523 |
524 | method decode_number.
525 | data: characters type table of c.
526 |
527 | while position < length.
528 | if json+position(1) na '0123456789.+-eE'.
529 | exit.
530 | else.
531 | append json+position(1) to characters.
532 | endif.
533 | add 1 to position.
534 | endwhile.
535 |
536 | concatenate lines of characters into string respecting blanks.
537 | endmethod.
538 |
539 |
540 | method DECODE_OBJECT.
541 | data: abap_name type string,
542 | dummy type syst.
543 | field-symbols: type any.
544 |
545 | if name is initial.
546 | assign value to .
547 | else.
548 | abap_name = name. translate abap_name to upper case.
549 | assign component abap_name of structure value to .
550 | if sy-subrc <> 0.
551 | assign dummy to .
552 | endif.
553 | endif.
554 |
555 | add 1 to position.
556 |
557 | "// Decode member value
558 | while position < length and
559 | json+position(1) <> '}'.
560 |
561 | decode_anything(
562 | exporting
563 | json = json
564 | length = length
565 | changing
566 | position = position
567 | value =
568 | ).
569 |
570 | "// Skip padding characters
571 | skip_to_next_character.
572 | check position < length.
573 | endwhile.
574 | add 1 to position.
575 | endmethod.
576 |
577 |
578 | method decode_string.
579 | data: characters type table of c,
580 | unicode_hexc(4) type c,
581 | unicode_hex(4) type x.
582 |
583 | field-symbols: type c.
584 |
585 | add 1 to position.
586 | while position < length.
587 | case json+position(1).
588 | when '\'. "// Escaped character
589 | add 1 to position.
590 | case json+position(1).
591 | when '"'.
592 | append '"' to characters.
593 | when '\'.
594 | append '\' to characters.
595 | when '/'.
596 | append '/' to characters.
597 | when 'r'.
598 | append cr to characters.
599 | when 'n'.
600 | append lf to characters.
601 | when 'u'.
602 | add 1 to position.
603 | unicode_hexc = json+position(4).
604 | translate unicode_hexc to upper case.
605 | unicode_hex = unicode_hexc.
606 | assign unicode_hex to casting.
607 | append to characters.
608 | add 3 to position.
609 | endcase.
610 | when '"'. "// Finished string
611 | exit.
612 | when others.
613 | append json+position(1) to characters.
614 | endcase.
615 |
616 | add 1 to position.
617 | endwhile.
618 | add 1 to position.
619 |
620 | concatenate lines of characters into string respecting blanks.
621 | endmethod.
622 |
623 |
624 | method ENCODE.
625 | "// Encode passed data object to normal JSON
626 | json = encode_anything(
627 | name = name
628 | value = value
629 | ).
630 |
631 | "// Format generated JSON string
632 | if json is not initial.
633 | "// Apply indented style
634 | if me->pretty_print is not initial.
635 | json = pretty_print_json( json ).
636 | endif.
637 | else.
638 | if name is not initial.
639 | concatenate name ': {}' into json.
640 | else.
641 | json = '{}'.
642 | endif.
643 | endif.
644 | endmethod.
645 |
646 |
647 | method ENCODE_ANYTHING.
648 | data: type type ref to cl_abap_typedescr,
649 | contents_json type string.
650 |
651 | "// Check if this should be included
652 | check value is not initial or me->include_empty_values is not initial.
653 |
654 | "// Get data object type
655 | type = cl_abap_typedescr=>describe_by_data( value ).
656 |
657 | case type->type_kind.
658 | "// Object references
659 | when cl_abap_typedescr=>typekind_oref.
660 | json = encode_object(
661 | name = name
662 | type = type
663 | value = value
664 | ).
665 |
666 | "// Structures
667 | when cl_abap_typedescr=>typekind_struct1 or
668 | cl_abap_typedescr=>typekind_struct2.
669 | json = encode_structure(
670 | name = name
671 | type = type
672 | value = value
673 | ).
674 |
675 | "// Tables
676 | when cl_abap_typedescr=>typekind_table.
677 | json = encode_table(
678 | name = name
679 | type = type
680 | value = value
681 | ).
682 |
683 | "// Fields
684 | when others.
685 | json = encode_field(
686 | name = name
687 | type = type
688 | value = value
689 | ).
690 | endcase.
691 | endmethod.
692 |
693 |
694 | method encode_field.
695 | data: formatted_value type string,
696 | relative_name type string.
697 |
698 | "// Timestamp? (becomes ISO-8601)
699 | relative_name = type->get_relative_name( ).
700 | if relative_name = 'TIMESTAMP'.
701 | formatted_value = timestamp_sap_to_iso( value ).
702 | else.
703 | case type->type_kind.
704 | "// Date fields (become ISO-8601)
705 | when cl_abap_typedescr=>typekind_date.
706 | formatted_value = date_sap_to_iso( value ).
707 |
708 | "// Time fields (become ISO-8601)
709 | when cl_abap_typedescr=>typekind_time.
710 | formatted_value = time_sap_to_iso( value ).
711 |
712 | "// Static fields (don't need conversion)
713 | when cl_abap_typedescr=>typekind_num or
714 | cl_abap_typedescr=>typekind_hex or
715 | cl_abap_typedescr=>typekind_string or
716 | cl_abap_typedescr=>typekind_xstring.
717 | formatted_value = value.
718 |
719 | "// Numeric fields
720 | when cl_abap_typedescr=>typekind_packed or
721 | cl_abap_typedescr=>typekind_float or
722 | cl_abap_typedescr=>typekind_int or
723 | cl_abap_typedescr=>typekind_int1 or
724 | cl_abap_typedescr=>typekind_int2 or
725 | cl_abap_typedescr=>typekind_numeric.
726 | formatted_value = value.
727 | translate formatted_value using '- + '.
728 | condense formatted_value no-gaps.
729 | if value < 0.
730 | concatenate '-' formatted_value into formatted_value.
731 | endif.
732 |
733 | "// Anything else gets the default SAP output conversion
734 | when others.
735 | formatted_value = value.
736 | if me->use_conversion_exits is not initial.
737 | call_conversion_exit(
738 | exporting direction = 1
739 | type = type
740 | value = value
741 | changing result = formatted_value
742 | ).
743 | endif.
744 |
745 | endcase.
746 | endif.
747 | formatted_value = escape_string( formatted_value ).
748 |
749 | "// Build JSON string
750 | if me->lowercase_names is not initial.
751 | translate name to lower case.
752 | endif.
753 | concatenate '"' name '": "' formatted_value '"' into json.
754 | endmethod.
755 |
756 |
757 | method ENCODE_OBJECT.
758 | data: ref type ref to cl_abap_refdescr,
759 | obj type ref to cl_abap_objectdescr,
760 | attributes_json type table of string,
761 | attribute_name type string,
762 | json_line type string.
763 |
764 | field-symbols: like line of obj->attributes,
765 | type any.
766 |
767 | "// Encode all obj attributes
768 | ref ?= type.
769 | obj ?= ref->get_referenced_type( ).
770 |
771 | loop at obj->attributes assigning
772 | where visibility = cl_abap_classdescr=>public.
773 | concatenate 'value->' -name into attribute_name.
774 | assign (attribute_name) to .
775 | if sy-subrc = 0.
776 | json_line = encode_anything(
777 | name = -name
778 | value =
779 | ).
780 | if json_line is not initial.
781 | append json_line to attributes_json.
782 | endif.
783 | endif.
784 | endloop.
785 |
786 | "// Build JSON string
787 | concatenate lines of attributes_json into json
788 | separated by ','.
789 | if name is not initial.
790 | if me->lowercase_names is not initial.
791 | translate name to lower case.
792 | endif.
793 | concatenate '"' name '": {' json '}' into json.
794 | else.
795 | concatenate '{' json '}' into json.
796 | endif.
797 | endmethod.
798 |
799 |
800 | method ENCODE_STRUCTURE.
801 | data: struct type ref to cl_abap_structdescr,
802 | fields_json type table of string,
803 | field_name type string,
804 | json_line type string.
805 |
806 | field-symbols: like line of struct->components,
807 | type any.
808 |
809 | "// Encode all class attributes
810 | struct ?= type.
811 |
812 | loop at struct->components assigning .
813 | assign component -name of structure value
814 | to .
815 | if sy-subrc = 0.
816 | json_line = encode_anything(
817 | name = -name
818 | value =
819 | ).
820 | if json_line is not initial.
821 | append json_line to fields_json.
822 | endif.
823 | endif.
824 | endloop.
825 |
826 | "// Build JSON string
827 | concatenate lines of fields_json into json
828 | separated by ','.
829 | if name is not initial.
830 | if me->lowercase_names is not initial.
831 | translate name to lower case.
832 | endif.
833 | concatenate '"' name '": {' json '}' into json.
834 | else.
835 | concatenate '{' json '}' into json.
836 | endif.
837 | endmethod.
838 |
839 |
840 | method ENCODE_TABLE.
841 | data: table type ref to cl_abap_tabledescr,
842 | lines_json type table of string,
843 | json_line type string.
844 |
845 | field-symbols: type any table,
846 | type any.
847 |
848 | "// Encode all table lines
849 | table ?= type.
850 |
851 | assign value to .
852 | loop at assigning .
853 | json_line = encode_anything(
854 | value =
855 | ).
856 | if json_line is not initial.
857 | append json_line to lines_json.
858 | endif.
859 | endloop.
860 |
861 | "// Build JSON string
862 | concatenate lines of lines_json into json
863 | separated by ','.
864 | if name is not initial.
865 | if me->lowercase_names is not initial.
866 | translate name to lower case.
867 | endif.
868 | concatenate '"' name '": [' json ']' into json.
869 | else.
870 | concatenate '[' json ']' into json.
871 | endif.
872 | endmethod.
873 |
874 |
875 | method ESCAPE_STRING.
876 | data: cr type c,
877 | lf type c.
878 |
879 | field-symbols type x.
880 |
881 | assign cr to casting. = 13.
882 | assign lf to casting. = 10.
883 |
884 | result = input.
885 | replace regex '\s+$' in result with ''.
886 | replace all occurrences of '\' in result with '\\'.
887 | replace all occurrences of cr in result with '\r'.
888 | replace all occurrences of lf in result with '\n'.
889 | replace all occurrences of '"' in result with '\"'.
890 | endmethod.
891 |
892 |
893 | method PRETTY_PRINT_JSON.
894 | data: input_length type i,
895 | input_pos type i,
896 | prev_input_char type c,
897 | input_char type c,
898 | next_input_pos type i,
899 | next_input_char type c,
900 | in_string type flag,
901 | skip_chars type i,
902 | result_pos type i,
903 | indent_level type i,
904 | start_new_line_before type flag,
905 | start_new_line_after type flag,
906 | result type table of string,
907 | result_line(1024) type c.
908 |
909 | "// Go through the input string and ident it, creating a line table
910 | input_length = strlen( json_in ).
911 | do input_length times.
912 | input_char = json_in+input_pos(1).
913 | next_input_pos = input_pos + 1.
914 | if next_input_pos < input_length.
915 | next_input_char = json_in+next_input_pos(1).
916 | else.
917 | clear next_input_char.
918 | endif.
919 |
920 | if skip_chars = 0.
921 | case input_char.
922 | "// Escaped character
923 | when '\'.
924 | skip_chars = 1.
925 | if next_input_char = 'u'.
926 | skip_chars = 5.
927 | endif.
928 |
929 | "// String
930 | when '"'.
931 | if in_string is initial.
932 | in_string = 'X'.
933 | else.
934 | clear in_string.
935 | endif.
936 |
937 | "// Opening blocks
938 | when '{' or '['.
939 | if in_string is initial.
940 | add 1 to indent_level.
941 | if next_input_char <> '}' and
942 | next_input_char <> ']'.
943 | start_new_line_after = 'X'.
944 | endif.
945 | endif.
946 |
947 | "// Closing blocks
948 | when '}' or ']'.
949 | if in_string is initial.
950 | subtract 1 from indent_level.
951 | if prev_input_char <> '{' and
952 | prev_input_char <> '['.
953 | start_new_line_before = 'X'.
954 | endif.
955 | endif.
956 |
957 | "// Between members
958 | when ','.
959 | if in_string is initial.
960 | start_new_line_after = 'X'.
961 | endif.
962 | endcase.
963 | else.
964 | subtract 1 from skip_chars.
965 | endif.
966 |
967 | if start_new_line_before is not initial.
968 | clear start_new_line_before.
969 | append result_line to result.
970 | clear result_line.
971 | result_pos = indent_level * 2.
972 | endif.
973 |
974 | result_line+result_pos = input_char.
975 | add 1 to result_pos.
976 |
977 | if start_new_line_after is not initial.
978 | clear start_new_line_after.
979 | append result_line to result.
980 | clear result_line.
981 | result_pos = indent_level * 2.
982 | endif.
983 |
984 | prev_input_char = input_char.
985 | input_pos = next_input_pos.
986 | enddo.
987 | append result_line to result.
988 |
989 | "// Glue the lines together
990 | concatenate lines of result into json_out
991 | separated by %_cr_lf.
992 | endmethod.
993 |
994 |
995 | method TIMESTAMP_ISO_TO_SAP.
996 | data: date_iso type string,
997 | time_iso type string,
998 | tsc(14) type c.
999 |
1000 | split timestamp at 'T' into date_iso time_iso.
1001 | check sy-subrc = 0.
1002 |
1003 | tsc(8) = date_iso_to_sap( date_iso ).
1004 | tsc+8(6) = time_iso_to_sap( time_iso ).
1005 | result = tsc.
1006 | endmethod.
1007 |
1008 |
1009 | method TIMESTAMP_SAP_TO_ISO.
1010 | data: begin of ts_split,
1011 | date type datum,
1012 | time type uzeit,
1013 | end of ts_split,
1014 | tsc(14) type n,
1015 | date_iso type string,
1016 | time_iso type string.
1017 |
1018 | ts_split = tsc = timestamp.
1019 | date_iso = date_sap_to_iso( ts_split-date ).
1020 | time_iso = time_sap_to_iso( ts_split-time ).
1021 |
1022 | concatenate date_iso 'T' time_iso into result.
1023 | endmethod.
1024 |
1025 |
1026 | method TIME_ISO_TO_SAP.
1027 | data: hour(2) type n,
1028 | min(2) type n,
1029 | sec(2) type n.
1030 |
1031 | "// ISO-8601 allowed formats:
1032 | "// hh:mm:ss or hh:mm or hhmmss or hhmm or hh
1033 | find regex '(\d{2}):?(\d{2})?:?(\d{2})?' in time
1034 | submatches hour min sec.
1035 |
1036 | concatenate hour min sec into result.
1037 | endmethod.
1038 |
1039 |
1040 | method TIME_SAP_TO_ISO.
1041 | concatenate time(2) ':' time+2(2) ':' time+4(2) into result.
1042 | endmethod.
1043 | ENDCLASS.
1044 |
--------------------------------------------------------------------------------