├── 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 | 28 | 35 | 42 | 43 | 44 | 45 | 52 | 63 | 64 |
encode 29 | Import parameters:
30 | CLIKE name (optional) - Object name
31 | ANY value - ABAP value to be encoded
32 |
33 | Return: STRING - JSON string. 34 |
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 |
decode 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 |
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 |
65 | 66 | #### Properties 67 | 68 | 69 | 70 | 71 | 75 | 76 | 77 | 78 | 82 | 83 | 84 | 85 | 89 | 90 | 91 | 92 | 97 | 98 |
include_empty_values 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 |
pretty_print 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 |
lowercase_names 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 |
use_conversion_exits 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 |
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 | --------------------------------------------------------------------------------