1 /**
2  * @file printer/json.c
3  * @author Radek Krejci <rkrejci@cesnet.cz>
4  * @brief JSON printer for libyang data structure
5  *
6  * Copyright (c) 2015 CESNET, z.s.p.o.
7  *
8  * This source code is licensed under BSD 3-Clause License (the "License").
9  * You may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     https://opensource.org/licenses/BSD-3-Clause
13  */
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <assert.h>
19 #include <inttypes.h>
20 
21 #include "common.h"
22 #include "printer.h"
23 #include "tree_data.h"
24 #include "resolve.h"
25 #include "tree_internal.h"
26 
27 #define INDENT ""
28 #define LEVEL (level*2)
29 
30 static int json_print_nodes(struct lyout *out, int level, const struct lyd_node *root, int withsiblings, int toplevel,
31                             int options);
32 
33 int
json_print_string(struct lyout * out,const char * text)34 json_print_string(struct lyout *out, const char *text)
35 {
36     unsigned int i, n;
37 
38     if (!text) {
39         return 0;
40     }
41 
42     ly_write(out, "\"", 1);
43     for (i = n = 0; text[i]; i++) {
44         const unsigned char ascii = text[i];
45         if (ascii < 0x20) {
46             /* control character */
47             n += ly_print(out, "\\u%.4X", ascii);
48         } else {
49             switch (ascii) {
50             case '"':
51                 n += ly_print(out, "\\\"");
52                 break;
53             case '\\':
54                 n += ly_print(out, "\\\\");
55                 break;
56             default:
57                 ly_write(out, &text[i], 1);
58                 n++;
59             }
60         }
61     }
62     ly_write(out, "\"", 1);
63 
64     return n + 2;
65 }
66 
67 static int
json_print_attrs(struct lyout * out,int level,const struct lyd_node * node,const struct lys_module * wdmod)68 json_print_attrs(struct lyout *out, int level, const struct lyd_node *node, const struct lys_module *wdmod)
69 {
70     struct lyd_attr *attr;
71     size_t len;
72     char *p;
73 
74     LY_PRINT_SET;
75 
76     if (wdmod) {
77         ly_print(out, "%*s\"%s:default\":\"true\"", LEVEL, INDENT, wdmod->name);
78         ly_print(out, "%s%s", node->attr ? "," : "", (level ? "\n" : ""));
79     }
80     for (attr = node->attr; attr; attr = attr->next) {
81         if (!attr->annotation) {
82             /* skip exception for the NETCONF's attribute since JSON is not defined for NETCONF */
83             continue;
84         }
85         if (lys_main_module(attr->annotation->module) != lys_main_module(node->schema->module)) {
86             ly_print(out, "%*s\"%s:%s\":", LEVEL, INDENT, attr->annotation->module->name, attr->name);
87         } else {
88             ly_print(out, "%*s\"%s\":", LEVEL, INDENT, attr->name);
89         }
90         /* leafref is not supported */
91         switch (attr->value_type) {
92         case LY_TYPE_BINARY:
93         case LY_TYPE_STRING:
94         case LY_TYPE_BITS:
95         case LY_TYPE_ENUM:
96         case LY_TYPE_INST:
97         case LY_TYPE_INT64:
98         case LY_TYPE_UINT64:
99         case LY_TYPE_DEC64:
100             json_print_string(out, attr->value_str);
101             break;
102 
103         case LY_TYPE_INT8:
104         case LY_TYPE_INT16:
105         case LY_TYPE_INT32:
106         case LY_TYPE_UINT8:
107         case LY_TYPE_UINT16:
108         case LY_TYPE_UINT32:
109         case LY_TYPE_BOOL:
110             ly_print(out, "%s", attr->value_str[0] ? attr->value_str : "null");
111             break;
112 
113         case LY_TYPE_IDENT:
114             p = strchr(attr->value_str, ':');
115             assert(p);
116             len = p - attr->value_str;
117             if (!strncmp(attr->value_str, attr->annotation->module->name, len)
118                     && !attr->annotation->module->name[len]) {
119                 /* do not print the prefix, it is the default prefix for this node */
120                 json_print_string(out, ++p);
121             } else {
122                 json_print_string(out, attr->value_str);
123             }
124             break;
125 
126         case LY_TYPE_EMPTY:
127             ly_print(out, "[null]");
128             break;
129 
130         default:
131             /* error */
132             LOGINT(node->schema->module->ctx);
133             return EXIT_FAILURE;
134         }
135 
136         ly_print(out, "%s%s", attr->next ? "," : "", (level ? "\n" : ""));
137     }
138 
139     LY_PRINT_RET(node->schema->module->ctx);
140 }
141 
142 static int
json_print_leaf(struct lyout * out,int level,const struct lyd_node * node,int onlyvalue,int toplevel,int options)143 json_print_leaf(struct lyout *out, int level, const struct lyd_node *node, int onlyvalue, int toplevel, int options)
144 {
145     struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node, *iter;
146     const struct lys_type *type;
147     const char *schema = NULL, *p, *mod_name;
148     const struct lys_module *wdmod = NULL;
149     LY_DATA_TYPE datatype;
150     size_t len;
151 
152     LY_PRINT_SET;
153 
154     if ((node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) ||
155             (!node->dflt && (options & LYP_WD_ALL_TAG) && lyd_wd_default(leaf))) {
156         /* we have implicit OR explicit default node */
157         /* get with-defaults module */
158         wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
159     }
160 
161     if (!onlyvalue) {
162         if (toplevel || !node->parent || nscmp(node, node->parent)) {
163             /* print "namespace" */
164             schema = lys_node_module(node->schema)->name;
165             ly_print(out, "%*s\"%s:%s\":%s", LEVEL, INDENT, schema, node->schema->name, (level ? " " : ""));
166         } else {
167             ly_print(out, "%*s\"%s\":%s", LEVEL, INDENT, node->schema->name, (level ? " " : ""));
168         }
169     }
170 
171     datatype = leaf->value_type;
172 contentprint:
173     switch (datatype) {
174     case LY_TYPE_BINARY:
175     case LY_TYPE_STRING:
176     case LY_TYPE_BITS:
177     case LY_TYPE_ENUM:
178     case LY_TYPE_INST:
179     case LY_TYPE_INT64:
180     case LY_TYPE_UINT64:
181     case LY_TYPE_UNION:
182     case LY_TYPE_DEC64:
183         json_print_string(out, leaf->value_str);
184         break;
185 
186     case LY_TYPE_INT8:
187     case LY_TYPE_INT16:
188     case LY_TYPE_INT32:
189     case LY_TYPE_UINT8:
190     case LY_TYPE_UINT16:
191     case LY_TYPE_UINT32:
192     case LY_TYPE_BOOL:
193         ly_print(out, "%s", leaf->value_str[0] ? leaf->value_str : "null");
194         break;
195 
196     case LY_TYPE_IDENT:
197         p = strchr(leaf->value_str, ':');
198         assert(p);
199         len = p - leaf->value_str;
200         mod_name = leaf->schema->module->name;
201         if (!strncmp(leaf->value_str, mod_name, len) && !mod_name[len]) {
202             /* do not print the prefix, it is the default prefix for this node */
203             json_print_string(out, ++p);
204         } else {
205             json_print_string(out, leaf->value_str);
206         }
207         break;
208 
209     case LY_TYPE_LEAFREF:
210         iter = (struct lyd_node_leaf_list *)leaf->value.leafref;
211         while (iter && (iter->value_type == LY_TYPE_LEAFREF)) {
212             iter = (struct lyd_node_leaf_list *)iter->value.leafref;
213         }
214         if (!iter) {
215             /* unresolved and invalid, but we can learn the correct type anyway */
216             type = lyd_leaf_type((struct lyd_node_leaf_list *)leaf);
217             if (!type) {
218                 /* error */
219                 return EXIT_FAILURE;
220             }
221             datatype = type->base;
222         } else {
223             datatype = iter->value_type;
224         }
225         goto contentprint;
226 
227     case LY_TYPE_EMPTY:
228         ly_print(out, "[null]");
229         break;
230 
231     case LY_TYPE_UNKNOWN:
232         if (leaf->value_str[0] == '\0') {
233             ly_write(out, "\"\"", 2);
234             break;
235         }
236         datatype = ((struct lys_node_leaf *)leaf->schema)->type.base;
237         goto contentprint;
238 
239     default:
240         /* error */
241         LOGINT(node->schema->module->ctx);
242         return EXIT_FAILURE;
243     }
244 
245     /* print attributes as sibling leafs */
246     if (!onlyvalue && (node->attr || wdmod)) {
247         if (schema) {
248             ly_print(out, ",%s%*s\"@%s:%s\":%s{%s", (level ? "\n" : ""), LEVEL, INDENT, schema, node->schema->name,
249                      (level ? " " : ""), (level ? "\n" : ""));
250         } else {
251             ly_print(out, ",%s%*s\"@%s\":%s{%s", (level ? "\n" : ""), LEVEL, INDENT, node->schema->name,
252                      (level ? " " : ""), (level ? "\n" : ""));
253         }
254         if (json_print_attrs(out, (level ? level + 1 : level), node, wdmod)) {
255             return EXIT_FAILURE;
256         }
257         ly_print(out, "%*s}", LEVEL, INDENT);
258     }
259 
260     LY_PRINT_RET(node->schema->module->ctx);
261 }
262 
263 static int
json_print_container(struct lyout * out,int level,const struct lyd_node * node,int toplevel,int options)264 json_print_container(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
265 {
266     const char *schema;
267 
268     LY_PRINT_SET;
269 
270     if (toplevel || !node->parent || nscmp(node, node->parent)) {
271         /* print "namespace" */
272         schema = lys_node_module(node->schema)->name;
273         ly_print(out, "%*s\"%s:%s\":%s{%s", LEVEL, INDENT, schema, node->schema->name, (level ? " " : ""), (level ? "\n" : ""));
274     } else {
275         ly_print(out, "%*s\"%s\":%s{%s", LEVEL, INDENT, node->schema->name, (level ? " " : ""), (level ? "\n" : ""));
276     }
277     if (level) {
278         level++;
279     }
280     if (node->attr) {
281         ly_print(out, "%*s\"@\":%s{%s", LEVEL, INDENT, (level ? " " : ""), (level ? "\n" : ""));
282         if (json_print_attrs(out, (level ? level + 1 : level), node, NULL)) {
283             return EXIT_FAILURE;
284         }
285         ly_print(out, "%*s}", LEVEL, INDENT);
286         ly_print(out, "%s%s", (node->child? "," : ""), (level ? "\n" : ""));
287     }
288     if (json_print_nodes(out, level, node->child, 1, 0, options)) {
289         return EXIT_FAILURE;
290     }
291     if (level) {
292         level--;
293     }
294     ly_print(out, "%*s}", LEVEL, INDENT);
295 
296     LY_PRINT_RET(node->schema->module->ctx);
297 }
298 
299 static int
json_print_leaf_list(struct lyout * out,int level,const struct lyd_node * node,int is_list,int toplevel,int options)300 json_print_leaf_list(struct lyout *out, int level, const struct lyd_node *node, int is_list, int toplevel, int options)
301 {
302     const char *schema = NULL;
303     const struct lyd_node *list = node;
304     int flag_empty = 0, flag_attrs = 0;
305 
306     LY_PRINT_SET;
307 
308     if (is_list && !list->child) {
309         /* empty, e.g. in case of filter */
310         flag_empty = 1;
311     }
312 
313     if (toplevel || !node->parent || nscmp(node, node->parent)) {
314         /* print "namespace" */
315         schema = lys_node_module(node->schema)->name;
316         ly_print(out, "%*s\"%s:%s\":", LEVEL, INDENT, schema, node->schema->name);
317     } else {
318         ly_print(out, "%*s\"%s\":", LEVEL, INDENT, node->schema->name);
319     }
320 
321     if (flag_empty) {
322         ly_print(out, "%s[]", (level ? " " : ""));
323         goto finish;
324     }
325     ly_print(out, "%s[%s", (level ? " " : ""), (level ? "\n" : ""));
326 
327     if (!is_list && level) {
328         ++level;
329     }
330 
331     while (list) {
332         if (is_list) {
333             /* list print */
334             if (level) {
335                 ++level;
336             }
337             ly_print(out, "%*s{%s", LEVEL, INDENT, (level ? "\n" : ""));
338             if (level) {
339                 ++level;
340             }
341             if (list->attr) {
342                 ly_print(out, "%*s\"@\":%s{%s", LEVEL, INDENT, (level ? " " : ""), (level ? "\n" : ""));
343                 if (json_print_attrs(out, (level ? level + 1 : level), list, NULL)) {
344                     return EXIT_FAILURE;
345                 }
346                 if (list->child) {
347                     ly_print(out, "%*s},%s", LEVEL, INDENT, (level ? "\n" : ""));
348                 } else {
349                     ly_print(out, "%*s}", LEVEL, INDENT);
350                 }
351             }
352             if (json_print_nodes(out, level, list->child, 1, 0, options)) {
353                 return EXIT_FAILURE;
354             }
355             if (level) {
356                 --level;
357             }
358             ly_print(out, "%*s}", LEVEL, INDENT);
359             if (level) {
360                 --level;
361             }
362         } else {
363             /* leaf-list print */
364             ly_print(out, "%*s", LEVEL, INDENT);
365             if (json_print_leaf(out, level, list, 1, toplevel, options)) {
366                 return EXIT_FAILURE;
367             }
368             if (list->attr) {
369                 flag_attrs = 1;
370             }
371         }
372         if (toplevel && !(options & LYP_WITHSIBLINGS)) {
373             /* if initially called without LYP_WITHSIBLINGS do not print other list entries */
374             break;
375         }
376         for (list = list->next; list && list->schema != node->schema; list = list->next);
377         if (list) {
378             ly_print(out, ",%s", (level ? "\n" : ""));
379         }
380     }
381 
382     if (!is_list && level) {
383         --level;
384     }
385 
386     ly_print(out, "%s%*s]", (level ? "\n" : ""), LEVEL, INDENT);
387 
388     /* attributes */
389     if (!is_list && flag_attrs) {
390         if (schema) {
391             ly_print(out, ",%s%*s\"@%s:%s\":%s[%s", (level ? "\n" : ""), LEVEL, INDENT, schema, node->schema->name,
392                      (level ? " " : ""), (level ? "\n" : ""));
393         } else {
394             ly_print(out, ",%s%*s\"@%s\":%s[%s", (level ? "\n" : ""), LEVEL, INDENT, node->schema->name,
395                      (level ? " " : ""), (level ? "\n" : ""));
396         }
397         if (level) {
398             level++;
399         }
400         for (list = node; list; ) {
401             if (list->attr) {
402                 ly_print(out, "%*s{%s", LEVEL, INDENT, (level ? " " : ""));
403                 if (json_print_attrs(out, 0, list, NULL)) {
404                     return EXIT_FAILURE;
405                 }
406                 ly_print(out, "%*s}", LEVEL, INDENT);
407             } else {
408                 ly_print(out, "%*snull", LEVEL, INDENT);
409             }
410 
411 
412             for (list = list->next; list && list->schema != node->schema; list = list->next);
413             if (list) {
414                 ly_print(out, ",%s", (level ? "\n" : ""));
415             }
416         }
417         if (level) {
418             level--;
419         }
420         ly_print(out, "%s%*s]", (level ? "\n" : ""), LEVEL, INDENT);
421     }
422 
423 finish:
424     LY_PRINT_RET(node->schema->module->ctx);
425 }
426 
427 static int
json_print_anydataxml(struct lyout * out,int level,const struct lyd_node * node,int toplevel,int options)428 json_print_anydataxml(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
429 {
430     struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
431     int is_object = 0;
432     char *buf;
433     const char *schema = NULL;
434 
435     LY_PRINT_SET;
436 
437     if (toplevel || !node->parent || nscmp(node, node->parent)) {
438         /* print "namespace" */
439         schema = lys_node_module(node->schema)->name;
440         ly_print(out, "%*s\"%s:%s\":", LEVEL, INDENT, schema, node->schema->name);
441     } else {
442         ly_print(out, "%*s\"%s\":", LEVEL, INDENT, node->schema->name);
443     }
444     if (level) {
445         level++;
446     }
447 
448     switch (any->value_type) {
449     case LYD_ANYDATA_DATATREE:
450         is_object = 1;
451         ly_print(out, "%s{%s", (level ? " " : ""), (level ? "\n" : ""));
452         /* do not print any default values nor empty containers */
453         if (json_print_nodes(out, level, any->value.tree, 1, 0,  LYP_WITHSIBLINGS | (options & ~LYP_NETCONF))) {
454             return EXIT_FAILURE;
455         }
456         break;
457     case LYD_ANYDATA_JSON:
458         if (level) {
459             ly_print(out, "\n");
460         }
461         if (any->value.str) {
462             ly_print(out, "%s", any->value.str);
463         }
464         if (level && (!any->value.str || (any->value.str[strlen(any->value.str) - 1] != '\n'))) {
465             /* do not print 2 newlines */
466             ly_print(out, "\n");
467         }
468         break;
469     case LYD_ANYDATA_XML:
470         lyxml_print_mem(&buf, any->value.xml, (level ? LYXML_PRINT_FORMAT | LYXML_PRINT_NO_LAST_NEWLINE : 0)
471                                                | LYXML_PRINT_SIBLINGS);
472         if (level) {
473             ly_print(out, " ");
474         }
475         json_print_string(out, buf);
476         free(buf);
477         break;
478     case LYD_ANYDATA_CONSTSTRING:
479     case LYD_ANYDATA_SXML:
480         if (level) {
481             ly_print(out, " ");
482         }
483         if (any->value.str) {
484             json_print_string(out, any->value.str);
485         } else {
486             ly_print(out, "\"\"");
487         }
488         break;
489     case LYD_ANYDATA_STRING:
490     case LYD_ANYDATA_SXMLD:
491     case LYD_ANYDATA_JSOND:
492     case LYD_ANYDATA_LYBD:
493     case LYD_ANYDATA_LYB:
494         /* other formats are not supported */
495         LOGINT(node->schema->module->ctx);
496         return EXIT_FAILURE;
497     }
498 
499     /* print attributes as sibling leaf */
500     switch(node->schema->nodetype) {
501     case LYS_ANYXML:
502         if (level) {
503             level--;
504         }
505         if (is_object) {
506              ly_print(out, "%*s}", LEVEL, INDENT);
507         }
508         if (node->attr) {
509             if (schema) {
510                 ly_print(out, ",%s%*s\"@%s:%s\":%s{%s", (level ? "\n" : ""), LEVEL, INDENT, schema, node->schema->name,
511                          (level ? " " : ""), (level ? "\n" : ""));
512             } else {
513                 ly_print(out, ",%s%*s\"@%s\":%s{%s", (level ? "\n" : ""), LEVEL, INDENT, node->schema->name,
514                          (level ? " " : ""), (level ? "\n" : ""));
515             }
516             if (json_print_attrs(out, (level ? level + 1 : level), node, NULL)) {
517                 return EXIT_FAILURE;
518             }
519             ly_print(out, "%*s}", LEVEL, INDENT);
520         }
521         break;
522     case LYS_ANYDATA:
523         if (node->attr) {
524             ly_print(out, ",%s%*s\"@\":%s{%s", (level ? "\n" : ""), LEVEL, INDENT, (level ? " " : ""), (level ? "\n" : ""));
525             if (json_print_attrs(out, (level ? level + 1 : level), node, NULL)) {
526                 return EXIT_FAILURE;
527             }
528             ly_print(out, "%*s}", LEVEL, INDENT);
529         }
530         if (level) {
531             level--;
532         }
533         if (is_object) {
534             ly_print(out, "%s%*s}", (level ? "\n" : ""), LEVEL, INDENT);
535         }
536         break;
537     default:
538         LOGINT(node->schema->module->ctx);
539         return EXIT_FAILURE;
540     }
541 
542     LY_PRINT_RET(node->schema->module->ctx);
543 }
544 
545 static int
json_print_nodes(struct lyout * out,int level,const struct lyd_node * root,int withsiblings,int toplevel,int options)546 json_print_nodes(struct lyout *out, int level, const struct lyd_node *root, int withsiblings, int toplevel, int options)
547 {
548     int comma_flag = 0;
549     const struct lyd_node *node, *iter;
550 
551     LY_PRINT_SET;
552 
553     LY_TREE_FOR(root, node) {
554         if (lyd_node_should_print(node, options)) {
555             /* wd says to print */
556             switch (node->schema->nodetype) {
557             case LYS_RPC:
558             case LYS_ACTION:
559             case LYS_NOTIF:
560             case LYS_CONTAINER:
561                 if (comma_flag) {
562                     /* print the previous comma */
563                     ly_print(out, ",%s", (level ? "\n" : ""));
564                 }
565                 if (json_print_container(out, level, node, toplevel, options)) {
566                     return EXIT_FAILURE;
567                 }
568                 break;
569             case LYS_LEAF:
570                 if (comma_flag) {
571                     /* print the previous comma */
572                     ly_print(out, ",%s", (level ? "\n" : ""));
573                 }
574                 if (json_print_leaf(out, level, node, 0, toplevel, options)) {
575                     return EXIT_FAILURE;
576                 }
577                 break;
578             case LYS_LEAFLIST:
579             case LYS_LIST:
580                 /* is it already printed? (root node is not) */
581                 for (iter = node->prev; iter->next && node != root; iter = iter->prev) {
582                     if (iter == node) {
583                         continue;
584                     }
585                     if (iter->schema == node->schema) {
586                         /* the list has alread some previous instance and therefore it is already printed */
587                         break;
588                     }
589                 }
590                 if (!iter->next || node == root) {
591                     if (comma_flag) {
592                         /* print the previous comma */
593                         ly_print(out, ",%s", (level ? "\n" : ""));
594                     }
595 
596                     /* print the list/leaflist */
597                     if (json_print_leaf_list(out, level, node, node->schema->nodetype == LYS_LIST ? 1 : 0, toplevel, options)) {
598                         return EXIT_FAILURE;
599                     }
600                 }
601                 break;
602             case LYS_ANYXML:
603             case LYS_ANYDATA:
604                 if (comma_flag) {
605                     /* print the previous comma */
606                     ly_print(out, ",%s", (level ? "\n" : ""));
607                 }
608                 if (json_print_anydataxml(out, level, node, toplevel, options)) {
609                     return EXIT_FAILURE;
610                 }
611                 break;
612             default:
613                 LOGINT(node->schema->module->ctx);
614                 return EXIT_FAILURE;
615             }
616 
617             comma_flag = 1;
618         }
619 
620         if (!withsiblings) {
621             break;
622         }
623     }
624     if (root && level) {
625         ly_print(out, "\n");
626     }
627 
628     LY_PRINT_RET(root ? root->schema->module->ctx : NULL);
629 }
630 
631 int
json_print_data(struct lyout * out,const struct lyd_node * root,int options)632 json_print_data(struct lyout *out, const struct lyd_node *root, int options)
633 {
634     const struct lyd_node *node, *next;
635     int level = 0, action_input = 0;
636 
637     LY_PRINT_SET;
638 
639     if (options & LYP_FORMAT) {
640         ++level;
641     }
642 
643     if (options & LYP_NETCONF) {
644         if (root->schema->nodetype != LYS_RPC) {
645             /* learn whether we are printing an action */
646             LY_TREE_DFS_BEGIN(root, next, node) {
647                 if (node->schema->nodetype == LYS_ACTION) {
648                     break;
649                 }
650                 LY_TREE_DFS_END(root, next, node);
651             }
652         } else {
653             node = root;
654         }
655 
656         if (node && (node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
657             if (node->child && (node->child->schema->parent->nodetype == LYS_OUTPUT)) {
658                 /* skip the container */
659                 root = node->child;
660             } else if (node->schema->nodetype == LYS_ACTION) {
661                 action_input = 1;
662             }
663         }
664     }
665 
666     /* start */
667     ly_print(out, "{%s", (level ? "\n" : ""));
668 
669     if (action_input) {
670         ly_print(out, "%*s\"yang:action\":%s{%s", LEVEL, INDENT, (level ? " " : ""), (level ? "\n" : ""));
671         if (level) {
672             ++level;
673         }
674     }
675 
676     /* content */
677     if (json_print_nodes(out, level, root, options & LYP_WITHSIBLINGS, 1, options)) {
678         return EXIT_FAILURE;
679     }
680 
681     if (action_input) {
682         if (level) {
683             --level;
684         }
685         ly_print(out, "%*s}%s", LEVEL, INDENT, (level ? "\n" : ""));
686     }
687 
688     /* end */
689     ly_print(out, "}%s", (level ? "\n" : ""));
690 
691     ly_print_flush(out);
692     LY_PRINT_RET(NULL);
693 }
694