From 9da1df12e5bef43b37dfac892c3755c765496d5e Mon Sep 17 00:00:00 2001 From: Zsolt Raduska Date: Thu, 8 Mar 2018 16:02:47 +0100 Subject: [PATCH] Add json parse and stringify function to jerryscript c api JerryScript-DCO-1.0-Signed-off-by: Zsolt Raduska rzsolt@inf.u-szeged.hu --- docs/02.API-REFERENCE.md | 70 ++++++++++ jerry-core/api/jerry.c | 57 ++++++++ .../ecma-builtin-helpers-json.c | 4 + .../builtin-objects/ecma-builtin-helpers.h | 3 + .../ecma/builtin-objects/ecma-builtin-json.c | 124 ++++++++++++------ jerry-core/include/jerryscript-core.h | 2 + tests/unit-core/test-api.c | 42 ++++++ 7 files changed, 264 insertions(+), 38 deletions(-) diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index dec8efa089..ee35965823 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -5561,3 +5561,73 @@ jerry_value_t jerry_get_typedarray_buffer (jerry_value_t value, **See also** - [jerry_create_typedarray](#jerry_create_typedarray) + +# JSON functions + +## jerry_json_parse + +**Summary** + +Returns the same result as JSON.parse ecmascript function. + +**Prototype** + +```c +jerry_value_t jerry_json_parse (const jerry_char_t *string_p, jerry_size_t string_size) +``` + +- `string_p` - a JSON string +- `string_size` - size of the string +- return + - jerry_value_t containing the same as json.parse() + - jerry_value_t containing error massage + +**Example** + +```c +{ + const char *data = "{\"name\": \"John\", \"age\": 5}"; + jerry_size_t str_length = (jerry_size_t)strlen (data); + jerry_value_t parsed_json = jerry_json_parse ((jerry_char_t*)data, str_length); + + // parsed_json now conatins all data stored in data_in_json + + jerry_release_value (parsed_json); +} +``` + +## jerry_stringify + + **Summary** + + Returns the same value as JSON.stringify() ecmascript function. + + **Prototype** + +```c +jerry_value_t jerry_json_stringfy (const jerry_value_t object_to_stringify) +``` + +- `object_to_stringify` - a jerry_value_t object to stringify +- return + - jerry_value_t containing the same as json.stringify() + - jerry_value_t containing error massage + +**Example** + +```c +{ + jerry_value_t obj = jerry_create_object (); + jerry_value_t key = jerry_create_string ((const jerry_char_t *) "name"); + jerry_value_t value = jerry_create_string ((const jerry_char_t *) "John"); + jerry_set_property (obj, key, value); + jerry_value_t stringified = jerry_json_stringfy (obj); + + //stringified now contains a json formated string + + jerry_release_value (obj); + jerry_release_value (key); + jerry_release_value (value); + jerry_release_value (stringified); +} +``` \ No newline at end of file diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 5b3d164c47..7fba07fc70 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -3334,6 +3334,63 @@ jerry_get_typedarray_buffer (jerry_value_t value, /**< TypedArray to get the arr #endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ } /* jerry_get_typedarray_buffer */ +/** + * Create an object from JSON + * + * Note: + * The returned value must be freed with jerry_release_value + * @return jerry_value_t from json formated string or an error massage + */ +jerry_value_t +jerry_json_parse (const jerry_char_t *string_p, /**< json string */ + jerry_size_t string_size) /**< json string size */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_JSON_BUILTIN + ecma_value_t ret_value = ecma_builtin_json_parse_buffer (string_p, string_size); + + if (ecma_is_value_undefined (ret_value)) + { + ret_value = jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG ("JSON string parse error."))); + } + + return ret_value; +#else /* CONFIG_DISABLE_JSON_BUILTIN */ + JERRY_UNUSED (string_p); + JERRY_UNUSED (string_size); + + return jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG ("The JSON has been disabled."))); +#endif /* !CONFIG_DISABLE_JSON_BUILTIN */ +} /* jerry_json_parse */ + +/** + * Create a Json formated string from an object + * + * Note: + * The returned value must be freed with jerry_release_value + * @return json formated jerry_value_t or an error massage + */ +jerry_value_t +jerry_json_stringfy (const jerry_value_t object_to_stringify) /**< a jerry_object_t to stringify */ +{ + jerry_assert_api_available (); +#ifndef CONFIG_DISABLE_JSON_BUILTIN + ecma_value_t ret_value = ecma_builtin_json_string_from_object (object_to_stringify); + + if (ecma_is_value_undefined (ret_value)) + { + ret_value = jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG ("JSON stringify error."))); + } + + return ret_value; +#else /* CONFIG_DISABLE_JSON_BUILTIN */ + JERRY_UNUSED (object_to_stringify); + + return jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG ("The JSON has been disabled."))); +#endif /* !CONFIG_DISABLE_JSON_BUILTIN */ +} /* jerry_json_stringfy */ + /** * @} */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c index acfdc97d9e..d964e4d2f9 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c @@ -18,6 +18,8 @@ #include "ecma-builtin-helpers.h" #include "lit-char-helpers.h" +#ifndef CONFIG_DISABLE_JSON_BUILTIN + /** \addtogroup ecma ECMA * @{ * @@ -221,6 +223,8 @@ ecma_builtin_helper_json_create_non_formatted_json (lit_utf8_byte_t left_bracket return ecma_make_string_value (result_str_p); } /* ecma_builtin_helper_json_create_non_formatted_json */ +#endif /* !CONFIG_DISABLE_JSON_BUILTIN */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index 42f074e847..1e14a86abb 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -152,6 +152,9 @@ typedef struct ecma_object_t *replacer_function_p; } ecma_json_stringify_context_t; +ecma_value_t ecma_builtin_json_parse_buffer (const lit_utf8_byte_t * str_start_p, + lit_utf8_size_t string_size); +ecma_value_t ecma_builtin_json_string_from_object (const ecma_value_t arg1); bool ecma_json_has_object_in_stack (ecma_json_occurence_stack_item_t *stack_p, ecma_object_t *object_p); bool ecma_has_string_value_in_collection (ecma_collection_header_t *collection_p, ecma_value_t string_value); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c index f6210b4219..1bcf1cd9d2 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c @@ -805,6 +805,35 @@ ecma_builtin_json_walk (ecma_object_t *reviver_p, /**< reviver function */ return ret_value; } /* ecma_builtin_json_walk */ +/** + * Function to set a string token from the given arguments, fills its fields and advances the string pointer. + * + * @return ecma_value_t containing an object or an error massage + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_builtin_json_parse_buffer (const lit_utf8_byte_t * str_start_p, /**< String to parse */ + lit_utf8_size_t string_size) /**< size of the string */ +{ + ecma_json_token_t token; + token.current_p = str_start_p; + token.end_p = str_start_p + string_size; + + ecma_value_t final_result = ecma_builtin_json_parse_value (&token); + + if (!ecma_is_value_undefined (final_result)) + { + ecma_builtin_json_parse_next_token (&token, false); + + if (token.type != end_token) + { + ecma_free_value (final_result); + final_result = ECMA_VALUE_UNDEFINED; + } + } + return final_result; +} /*ecma_builtin_json_parse_buffer*/ + /** * The JSON object's 'parse' routine * @@ -830,22 +859,7 @@ ecma_builtin_json_parse (ecma_value_t this_arg, /**< 'this' argument */ ECMA_STRING_TO_UTF8_STRING (string_p, str_start_p, string_size); - ecma_json_token_t token; - token.current_p = str_start_p; - token.end_p = str_start_p + string_size; - - ecma_value_t final_result = ecma_builtin_json_parse_value (&token); - - if (!ecma_is_value_undefined (final_result)) - { - ecma_builtin_json_parse_next_token (&token, false); - - if (token.type != end_token) - { - ecma_free_value (final_result); - final_result = ECMA_VALUE_UNDEFINED; - } - } + ecma_value_t final_result = ecma_builtin_json_parse_buffer (str_start_p, string_size); if (ecma_is_value_undefined (final_result)) { @@ -892,6 +906,61 @@ ecma_builtin_json_object (ecma_object_t *obj_p, ecma_json_stringify_context_t *c static ecma_value_t ecma_builtin_json_array (ecma_object_t *obj_p, ecma_json_stringify_context_t *context_p); +/** + * Helper function to stringify an object in JSON format representing an ecma_value. + * + * @return ecma_value_t string created from an abject formating by a given context + * Returned value must be freed with ecma_free_value. + * + */ +static ecma_value_t ecma_builtin_json_str_helper (const ecma_value_t arg1, /**< object argument */ + ecma_json_stringify_context_t context) /**< context argument */ +{ + ecma_value_t ret_value = ECMA_VALUE_EMPTY; + ecma_object_t *obj_wrapper_p = ecma_op_create_object_object_noarg (); + ecma_string_t *empty_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + ecma_value_t put_comp_val = ecma_op_object_put (obj_wrapper_p, + empty_str_p, + arg1, + false); + JERRY_ASSERT (ecma_is_value_true (put_comp_val)); + ecma_free_value (put_comp_val); + ECMA_TRY_CATCH (str_val, + ecma_builtin_json_str (empty_str_p, obj_wrapper_p, &context), + ret_value); + ret_value = ecma_copy_value (str_val); + ECMA_FINALIZE (str_val); + ecma_free_value (put_comp_val); + ecma_deref_ecma_string (empty_str_p); + ecma_deref_object (obj_wrapper_p); + + return ret_value; +} /* ecma_builtin_json_str_helper */ + +/** + * Function to create a json formated string from an object + * + * @return ecma_value_t containing a json string + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_builtin_json_string_from_object (const ecma_value_t arg1) /**< object argument */ +{ + ecma_json_stringify_context_t context; + context.occurence_stack_last_p = NULL; + context.indent_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + context.property_list_p = ecma_new_values_collection (); + context.replacer_function_p = NULL; + context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + + ecma_value_t ret_value = ecma_builtin_json_str_helper (arg1, context); + + ecma_deref_ecma_string (context.gap_str_p); + ecma_deref_ecma_string (context.indent_str_p); + ecma_free_values_collection (context.property_list_p, 0); + return ret_value; +} /*ecma_builtin_json_string_from_object*/ + /** * The JSON object's 'stringify' routine * @@ -1119,28 +1188,7 @@ ecma_builtin_json_stringify (ecma_value_t this_arg, /**< 'this' argument */ if (ecma_is_value_empty (ret_value)) { /* 9. */ - ecma_object_t *obj_wrapper_p = ecma_op_create_object_object_noarg (); - ecma_string_t *empty_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); - - /* 10. */ - ecma_value_t put_comp_val = ecma_op_object_put (obj_wrapper_p, - empty_str_p, - arg1, - false); - - JERRY_ASSERT (ecma_is_value_true (put_comp_val)); - ecma_free_value (put_comp_val); - - /* 11. */ - ECMA_TRY_CATCH (str_val, - ecma_builtin_json_str (empty_str_p, obj_wrapper_p, &context), - ret_value); - - ret_value = ecma_copy_value (str_val); - - ECMA_FINALIZE (str_val); - - ecma_deref_object (obj_wrapper_p); + ret_value = ecma_builtin_json_str_helper (arg1, context); } ecma_deref_ecma_string (context.gap_str_p); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index e58b7d644c..8f8ef3c334 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -522,6 +522,8 @@ jerry_length_t jerry_get_typedarray_length (jerry_value_t value); jerry_value_t jerry_get_typedarray_buffer (jerry_value_t value, jerry_length_t *byte_offset, jerry_length_t *byte_length); +jerry_value_t jerry_json_parse (const jerry_char_t *string_p, jerry_size_t string_size); +jerry_value_t jerry_json_stringfy (const jerry_value_t object_to_stringify); /** * @} diff --git a/tests/unit-core/test-api.c b/tests/unit-core/test-api.c index 948d198762..7ae9dc2edc 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -1130,6 +1130,48 @@ main (void) jerry_release_value (args[1]); + { + /*json parser check*/ + char data_check[]="John"; + jerry_value_t key = jerry_create_string ((const jerry_char_t *) "name"); + const char *data = "{\"name\": \"John\", \"age\": 5}"; + jerry_size_t str_length = (jerry_size_t) strlen (data); + jerry_value_t parsed_json = jerry_json_parse ((jerry_char_t *) data, str_length); + jerry_value_t has_prop_js = jerry_has_property (parsed_json, key); + TEST_ASSERT (jerry_get_boolean_value (has_prop_js)); + jerry_release_value (has_prop_js); + jerry_value_t parsed_data = jerry_get_property (parsed_json, key); + TEST_ASSERT (jerry_value_is_string (parsed_data)== true); + jerry_size_t buff_size = (jerry_size_t) jerry_get_string_length (parsed_data); + char buff[jerry_get_string_length (parsed_data)]; + jerry_char_t *buff_p = (jerry_char_t *) buff; + jerry_string_to_char_buffer (parsed_data, buff_p, buff_size); + buff[buff_size] = '\0'; + TEST_ASSERT (strcmp ((const char *) data_check, (const char *) buff) == false); + jerry_release_value (parsed_json); + jerry_release_value (key); + jerry_release_value (parsed_data); + } + + /*json stringify test*/ + { + jerry_value_t obj = jerry_create_object (); + char check_value[] = "{\"name\":\"John\"}"; + jerry_value_t key = jerry_create_string ((const jerry_char_t *) "name"); + jerry_value_t value = jerry_create_string ((const jerry_char_t *) "John"); + jerry_set_property (obj, key, value); + jerry_value_t stringified = jerry_json_stringfy (obj); + TEST_ASSERT (jerry_value_is_string (stringified)); + char buff[jerry_get_string_length (stringified)]; + jerry_string_to_char_buffer (stringified, (jerry_char_t *) buff, + (jerry_size_t) jerry_get_string_length (stringified)); + buff[jerry_get_string_length (stringified)] = '\0'; + TEST_ASSERT (strcmp ((const char *) check_value, (const char *) buff) == 0); + jerry_release_value (stringified); + jerry_release_value (obj); + jerry_release_value (key); + jerry_release_value (value); + } jerry_cleanup (); return 0;