Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/parser_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ struct ly_in;
#define LYD_PARSE_JSON_NULL 0x4000000 /**< Allow using JSON empty value 'null' within JSON input, such nodes are
silently skipped and treated as non-existent. By default, such values
are invalid. */

#define LYD_PARSE_JSON_STRING_DATATYPES 0x8000000 /**< By default, JSON data values are expected to be in the correct
format according to RFC 7951 based on their type. Using this
option the validation can be softened to accept boolean and
number type values enclosed in quotes. */
#define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */

/** @} dataparseroptions */
Expand Down
27 changes: 17 additions & 10 deletions src/parser_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,16 +340,18 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
/**
* @brief Get the hint for the data type parsers according to the current JSON parser context.
*
* @param[in] jsonctx JSON parser context. The context is supposed to be on a value.
* @param[in] lydctx JSON data parser context.
* @param[in,out] status Pointer to the current context status,
* in some circumstances the function manipulates with the context so the status is updated.
* @param[out] type_hint_p Pointer to the variable to store the result.
* @return LY_SUCCESS in case of success.
* @return LY_EINVAL in case of invalid context status not referring to a value.
*/
static LY_ERR
lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
{
struct lyjson_ctx *jsonctx = lydctx->jsonctx;

*type_hint_p = 0;

if (*status_p == LYJSON_ARRAY) {
Expand Down Expand Up @@ -383,6 +385,10 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
return LY_EINVAL;
}

if (lydctx->parse_opts & LYD_PARSE_JSON_STRING_DATATYPES) {
*type_hint_p |= LYD_VALHINT_STRING_DATATYPES;
}

return LY_SUCCESS;
}

Expand All @@ -391,15 +397,16 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s
*
* Checks for all the list's keys. Function does not revert the context state.
*
* @param[in] jsonctx JSON parser context.
* @param[in] lydctx JSON data parser context.
* @param[in] list List schema node corresponding to the input data object.
* @return LY_SUCCESS in case the data are ok for the @p list
* @return LY_ENOT in case the input data are not sufficient to fully parse the list instance.
*/
static LY_ERR
lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list)
{
LY_ERR rc = LY_SUCCESS;
struct lyjson_ctx *jsonctx = lydctx->jsonctx;
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
struct ly_set key_set = {0};
const struct lysc_node *snode;
Expand Down Expand Up @@ -451,7 +458,7 @@ lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
goto cleanup;
}

rc = lydjson_value_type_hint(jsonctx, &status, &hints);
rc = lydjson_value_type_hint(lydctx, &status, &hints);
LY_CHECK_GOTO(rc, cleanup);
rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
LY_CHECK_GOTO(rc, cleanup);
Expand Down Expand Up @@ -521,7 +528,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
case LYS_LEAFLIST:
case LYS_LEAF:
/* value may not be valid in which case we parse it as an opaque node */
if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) {
break;
}

Expand All @@ -533,14 +540,14 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
break;
case LYS_LIST:
/* lists may not have all its keys */
if (lydjson_check_list(jsonctx, snode)) {
if (lydjson_check_list(lydctx, snode)) {
/* invalid list, parse as opaque if it misses/has invalid some keys */
ret = LY_ENOT;
}
break;
}
} else if (snode->nodetype & LYD_NODE_TERM) {
ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
}

/* restore parser */
Expand Down Expand Up @@ -852,7 +859,7 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);

/* get value hints */
LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup);

if (node->schema) {
/* create metadata */
Expand Down Expand Up @@ -981,7 +988,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
dynamic = lydctx->jsonctx->dynamic;
lydctx->jsonctx->dynamic = 0;

LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
}

/* get the module name */
Expand Down
9 changes: 6 additions & 3 deletions src/plugins_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
case LY_TYPE_INT32:
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);

if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) &&
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_len, value);
}
Expand All @@ -695,7 +696,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
case LY_TYPE_INT64:
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);

if (!(hints & LYD_VALHINT_NUM64)) {
if (!(hints & LYD_VALHINT_NUM64) &&
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_len, value);
}
Expand All @@ -714,7 +716,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D
}
break;
case LY_TYPE_BOOL:
if (!(hints & LYD_VALHINT_BOOLEAN)) {
if (!(hints & LYD_VALHINT_BOOLEAN) &&
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_len, value);
}
Expand Down
1 change: 1 addition & 0 deletions src/tree_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ struct lyd_node_any {
#define LYD_VALHINT_NUM64 0x0010 /**< value is allowed to be an int64 or uint64 */
#define LYD_VALHINT_BOOLEAN 0x0020 /**< value is allowed to be a boolean */
#define LYD_VALHINT_EMPTY 0x0040 /**< value is allowed to be empty */
#define LYD_VALHINT_STRING_DATATYPES 0x0080 /**< boolean and numeric fields are allowed to be quoted */
/**
* @} lydvalhints
*/
Expand Down
15 changes: 15 additions & 0 deletions tests/utests/data/test_parser_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,21 @@ test_leaf(void **state)
PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1);
CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree);
assert_null(tree);

/* validate integer in quotes errors out by default */
data = "{\"a:foo3\":\"1234\"}";
PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
"Invalid non-number-encoded uint32 value \"1234\".", "/a:foo3", 1);

/* validate integers are parsed correctly */
data = "{\"a:foo3\":1234}";
CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
lyd_free_all(tree);

/* validate LYD_PARSE_JSON_STRING_DATATYPES parser flag allows integers in quotes */
data = "{\"a:foo3\":\"1234\"}";
CHECK_PARSE_LYD(data, LYD_PARSE_JSON_STRING_DATATYPES, LYD_VALIDATE_PRESENT, tree);
lyd_free_all(tree);
}

static void
Expand Down