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