json BUGFIX proper array parse and print
Includes the special [null] array and
any lists (array of objects) and
leaf-lists (array of values).
Refs cesnet/libnetconf2#331
diff --git a/src/parser_json.c b/src/parser_json.c
index 81d1842..e9cbfe9 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -962,17 +962,35 @@
ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p);
LY_CHECK_RET(ret);
+ if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) {
+ /* special array null value */
+ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY;
+
+ /* must be the only item */
+ LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member.");
+ return LY_EVALID;
+ }
+
+ goto finish;
+ }
+
while ((*status_p == LYJSON_ARRAY) || (*status_p == LYJSON_ARRAY_EMPTY)) {
/* process another instance of the same node */
- /* but first mark the node to be expected a list or a leaf-list */
- ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST;
if ((*status_inner_p == LYJSON_OBJECT) || (*status_inner_p == LYJSON_OBJECT_EMPTY)) {
+ /* array with objects, list */
+ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST;
+
/* but first process children of the object in the array */
- while (*status_inner_p != LYJSON_OBJECT_CLOSED && *status_inner_p != LYJSON_OBJECT_EMPTY) {
+ while ((*status_inner_p != LYJSON_OBJECT_CLOSED) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
*status_inner_p = lyjson_ctx_status(lydctx->jsonctx, 0);
}
+ } else {
+ /* array with values, leaf-list */
+ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST;
}
LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
diff --git a/src/printer_json.c b/src/printer_json.c
index dea32d3..0682309 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -601,12 +601,13 @@
has_content = 1;
}
- if (!node->schema || (node->schema->nodetype != LYS_LIST)) {
+ if ((node->schema && (node->schema->nodetype == LYS_LIST)) ||
+ (!node->schema && (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST))) {
+ ly_print_(ctx->out, "%s%*s{%s", (is_open_array(ctx, node) && ctx->level_printed >= ctx->level) ?
+ (DO_FORMAT ? ",\n" : ",") : "", INDENT, (DO_FORMAT && has_content) ? "\n" : "");
+ } else {
ly_print_(ctx->out, "%s{%s", (is_open_array(ctx, node) && ctx->level_printed >= ctx->level) ? "," : "",
(DO_FORMAT && has_content) ? "\n" : "");
- } else {
- ly_print_(ctx->out, "%s%*s{%s", (is_open_array(ctx, node) && ctx->level_printed >= ctx->level) ? (DO_FORMAT ? ",\n" : ",") : "",
- INDENT, (DO_FORMAT && has_content) ? "\n" : "");
}
LEVEL_INC;
@@ -772,10 +773,14 @@
if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
LY_CHECK_RET(json_print_array_open(ctx, &node->node));
- LEVEL_INC;
}
+ if (node->hints & LYD_NODEHINT_LEAFLIST) {
+ ly_print_(ctx->out, "%*s", INDENT);
+ }
+ } else if (node->hints & LYD_NODEHINT_LEAFLIST) {
+ ly_print_(ctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
}
- if (node->child || (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
+ if (node->child || (node->hints & LYD_NODEHINT_LIST)) {
LY_CHECK_RET(json_print_inner(ctx, &node->node));
LEVEL_PRINTED;
} else {
@@ -795,7 +800,6 @@
}
if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
json_print_array_close(ctx);
- LEVEL_DEC;
LEVEL_PRINTED;
}
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index 9831cf8..6a50913 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -380,7 +380,7 @@
"Invalid non-number-encoded uint32 value \"\".", "Schema location /a:foo3, line number 1.");
/* opaq flag */
- CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, LYD_VALIDATE_PRESENT, tree);
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo3", 0, 0, NULL, 0, "");
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
@@ -391,7 +391,7 @@
"List instance is missing its key \"c\".", "Schema location /a:l1, data location /a:l1[a='val_a'][b='val_b'], line number 1.");
/* opaq flag */
- CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, LYD_VALIDATE_PRESENT, tree);
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
@@ -402,19 +402,19 @@
"Invalid non-number-encoded int16 value \"val_c\".", "Schema location /a:l1/c, data location /a:l1[a='val_a'][b='val_b'], line number 1.");
/* opaq flag */
- CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, LYD_VALIDATE_PRESENT, tree);
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":{\"val\":\"val_c\"}}]}";
- CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, LYD_VALIDATE_PRESENT, tree);
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\"}]}";
- CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, LYD_VALIDATE_PRESENT, tree);
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_JSON, "l1", 0, 0, NULL, 0, "");
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
@@ -427,6 +427,66 @@
/* empty name */
PARSER_CHECK_ERROR("{\"@a:foo\":{\"\":0}}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
"A JSON object member name cannot be a zero-length string.", "Line number 1.");
+
+ /* opaque data tree format print */
+ data =
+ "{\n"
+ " \"ietf-netconf-nmda:get-data\": {\n"
+ " \"data\": {\n"
+ " \"ietf-keystore:keystore\": {\n"
+ " \"asymmetric-keys\": {\n"
+ " \"asymmetric-key\": [\n"
+ " {\n"
+ " \"name\": \"genkey\",\n"
+ " \"algorithm\": \"rsa2048\"\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " },\n"
+ " \"ietf-netconf-server:netconf-server\": {\n"
+ " \"listen\": {\n"
+ " \"idle-timeout\": 3600,\n"
+ " \"endpoint\": [\n"
+ " {\n"
+ " \"name\": \"default-ssh\",\n"
+ " \"ssh\": {\n"
+ " \"tcp-server-parameters\": {\n"
+ " \"local-address\": \"0.0.0.0\",\n"
+ " \"local-port\": 830\n"
+ " },\n"
+ " \"ssh-server-parameters\": {\n"
+ " \"server-identity\": {\n"
+ " \"host-key\": [\n"
+ " {\n"
+ " \"name\": \"default-key\",\n"
+ " \"public-key\": {\n"
+ " \"keystore-reference\": \"genkey\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"client-authentication\": {\n"
+ " \"supported-authentication-methods\": {\n"
+ " \"publickey\": [null],\n"
+ " \"passsword\": [null],\n"
+ " \"other\": [\n"
+ " \"interactive\",\n"
+ " \"gssapi\"\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
}
static void