blob: 171d30f416ba006e1f46db344e9705c0160fa108 [file] [log] [blame]
Radek Krejci13a57b62019-07-19 13:04:09 +02001/**
Michal Vasko90932a92020-02-12 14:33:03 +01002 * @file printer_json.c
Radek Krejci13a57b62019-07-19 13:04:09 +02003 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko90932a92020-02-12 14:33:03 +01004 * @brief JSON printer for libyang data structure
Radek Krejci13a57b62019-07-19 13:04:09 +02005 *
Radek Krejci5536d282020-08-04 23:27:44 +02006 * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
Radek Krejci13a57b62019-07-19 13:04:09 +02007 *
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
Radek Krejci5536d282020-08-04 23:27:44 +020015#include <assert.h>
16#include <stdlib.h>
17#include <string.h>
18
Radek Krejci13a57b62019-07-19 13:04:09 +020019#include "common.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020020#include "config.h"
Radek Krejci5536d282020-08-04 23:27:44 +020021#include "log.h"
22#include "parser_data.h"
23#include "plugins_types.h"
24#include "printer_data.h"
25#include "printer_internal.h"
26#include "set.h"
27#include "tree.h"
28#include "tree_data.h"
29#include "tree_data_internal.h"
Radek Krejci13a57b62019-07-19 13:04:09 +020030#include "tree_schema.h"
31
32/**
Radek Krejci5536d282020-08-04 23:27:44 +020033 * @brief JSON printer context.
34 */
35struct jsonpr_ctx {
36 struct ly_out *out; /**< output specification */
37 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
38 int options; /**< [Data printer flags](@ref dataprinterflags) */
39 const struct ly_ctx *ctx; /**< libyang context */
40
41 unsigned int level_printed; /* level where some dara were already printed */
42 struct ly_set open; /* currently open array(s) */
43 const struct lyd_node *print_sibling_metadata;
44};
45
46/**
47 * @brief Mark that something was already written in the current level,
48 * used to decide if a comma is expected between the items
49 */
50#define LEVEL_PRINTED ctx->level_printed = ctx->level
51
52#define PRINT_COMMA \
53 if (ctx->level_printed >= ctx->level) {\
54 ly_print(ctx->out, ",%s", (DO_FORMAT ? "\n" : ""));\
55 }
56
57static LY_ERR json_print_node(struct jsonpr_ctx *ctx, const struct lyd_node *node);
58
59/**
Radek Krejcia1911222019-07-22 17:24:50 +020060 * @brief JSON mapping of YANG modules to prefixes in values.
Radek Krejci13a57b62019-07-19 13:04:09 +020061 *
Radek Krejci1798aae2020-07-14 13:26:06 +020062 * Implementation of ly_get_prefix_clb.
Radek Krejci13a57b62019-07-19 13:04:09 +020063 */
64const char *
65json_print_get_prefix(const struct lys_module *mod, void *UNUSED(private))
66{
67 return mod->name;
68}
69
Radek Krejci5536d282020-08-04 23:27:44 +020070/**
71 * Compare 2 nodes, despite it is regular data node or an opaq node, and
72 * decide if they corresponds to the same schema node.
73 *
74 * TODO: rewrite lyd_compare_single and use it instead of this
75 *
76 * @return 1 - matching nodes, 0 - non-matching nodes
77 */
78static int
79matching_node(const struct lyd_node *node1, const struct lyd_node *node2)
80{
81 assert(node1 || node2);
82
83 if (!node1 || !node2) {
84 return 0;
85 } else if (node1->schema != node2->schema) {
86 return 0;
87 }
88 if (!node1->schema) {
89 /* compare node names */
90 struct lyd_node_opaq *onode1 = (struct lyd_node_opaq*)node1;
91 struct lyd_node_opaq *onode2 = (struct lyd_node_opaq*)node2;
92 if (onode1->name != onode2->name || onode1->prefix.id != onode2->prefix.id) {
93 return 0;
94 }
95 }
96
97 return 1;
98}
99
100/**
101 * @brief Open the JSON array ('[') for the specified @p node
102 *
103 * @param[in] ctx JSON printer context.
104 * @param[in] node First node of the array.
105 */
106static void
107json_print_array_open(struct jsonpr_ctx *ctx, const struct lyd_node *node)
108{
109 /* leaf-list's content is always printed on a single line */
110 ly_print(ctx->out, "[%s", (!node->schema || node->schema->nodetype != LYS_LEAFLIST) && DO_FORMAT ? "\n" : "");
111 ly_set_add(&ctx->open, (void*)node, 0);
112 LEVEL_INC;
113}
114
115/**
116 * @brief Get know if the array for the provided @p node is currently open.
117 *
118 * @param[in] ctx JSON printer context.
119 * @param[in] node Data node to check.
120 * @return 1 in case the printer is currently in the array belonging to the provided @p node.
121 * @return 0 in case the provided @p node is not connected with the currently open array (or there is no open array).
122 */
123static int
124is_open_array(struct jsonpr_ctx *ctx, const struct lyd_node *node)
125{
126 if (ctx->open.count && matching_node(node, (const struct lyd_node*)ctx->open.objs[ctx->open.count - 1])) {
127 return 1;
128 } else {
129 return 0;
130 }
131}
132
133/**
134 * @brief Close the most inner JSON array.
135 *
136 * @param[in] ctx JSON printer context.
137 */
138static void
139json_print_array_close(struct jsonpr_ctx *ctx)
140{
141 const struct lysc_node *schema = ((const struct lyd_node*)ctx->open.objs[ctx->open.count - 1])->schema;
142
143 LEVEL_DEC;
144 ly_set_rm_index(&ctx->open, ctx->open.count - 1, NULL);
145 if (schema && schema->nodetype == LYS_LEAFLIST) {
146 /* leaf-list's content is always printed on a single line */
147 ly_print(ctx->out, "]");
148 } else {
149 ly_print(ctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
150 }
151}
152
153/**
154 * @brief Get the node's module name to use as the @p node prefix in JSON.
155 * @param[in] node Node to process.
156 * @return The name of the module where the @p node belongs, it can be NULL in case the module name
157 * cannot be determined (source format is XML and the refered namespace is unknown/not implemented in the current context).
158 */
159static const char *
160node_prefix(const struct lyd_node *node)
161{
162 if (node->schema) {
163 return node->schema->module->name;
164 } else {
165 struct lyd_node_opaq *onode = (struct lyd_node_opaq*)node;
166 const struct lys_module *mod;
167
168 switch (onode->format) {
169 case LYD_JSON:
170 return onode->prefix.module_name;
171 case LYD_XML:
172 mod = ly_ctx_get_module_implemented_ns(onode->ctx, onode->prefix.module_ns);
173 if (!mod) {
174 return NULL;
175 }
176 return mod->name;
177 case LYD_SCHEMA:
178 case LYD_LYB:
179 /* cannot be created */
180 LOGINT(LYD_NODE_CTX(node));
181 }
182 }
183
184 return NULL;
185}
186
187/**
188 * @brief Compare 2 nodes if the belongs to the same module (if they come from the same namespace)
189 *
190 * Accepts both regulard a well as opaq nodes.
191 *
192 * @param[in] node1 The first node to compare.
193 * @param[in] node2 The second node to compare.
194 * @return 0 in case the nodes' modules are the same
195 * @return 1 in case the nodes belongs to different modules
196 */
197int
198json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
199{
200 assert(node1 || node2);
201
202 if (!node1 || !node2) {
203 return 1;
204 } else if (node1->schema && node2->schema) {
205 if (node1->schema->module == node2->schema->module) {
206 /* belongs to the same module */
207 return 0;
208 } else {
209 /* different modules */
210 return 1;
211 }
212 } else {
213 const char *pref1 = node_prefix(node1);
214 const char *pref2 = node_prefix(node2);
215 if ((pref1 && pref2) && pref1 == pref2) {
216 return 0;
217 } else {
218 return 1;
219 }
220 }
221}
222
223/**
224 * @brief Print the @p text as JSON string - encode special characters and add quotation around the string.
225 *
226 * @param[in] out The output handler.
227 * @param[in] text The string to print.
228 * @return The number of printed characters.
229 */
230static int
231json_print_string(struct ly_out *out, const char *text)
232{
233 unsigned int i, n;
234
235 if (!text) {
236 return 0;
237 }
238
239 ly_write(out, "\"", 1);
240 for (i = n = 0; text[i]; i++) {
241 const unsigned char ascii = text[i];
242 if (ascii < 0x20) {
243 /* control character */
244 n += ly_print(out, "\\u%.4X", ascii);
245 } else {
246 switch (ascii) {
247 case '"':
248 n += ly_print(out, "\\\"");
249 break;
250 case '\\':
251 n += ly_print(out, "\\\\");
252 break;
253 default:
254 ly_write(out, &text[i], 1);
255 n++;
256 }
257 }
258 }
259 ly_write(out, "\"", 1);
260
261 return n + 2;
262}
263
264/**
265 * @brief Print JSON object's member name, ending by ':'. It resolves if the prefix is supposed to be printed.
266 *
267 * @param[in] ctx JSON printer context.
268 * @param[in] node The data node being printed.
269 * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
270 * @return LY_ERR value.
271 */
272static LY_ERR
273json_print_member(struct jsonpr_ctx *ctx, const struct lyd_node *node, int is_attr)
274{
275 PRINT_COMMA;
276 if (LEVEL == 1 || json_nscmp(node, (const struct lyd_node*)node->parent)) {
277 /* print "namespace" */
278 ly_print(ctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "",
279 node_prefix(node), node->schema->name, DO_FORMAT ? " " : "");
280 } else {
281 ly_print(ctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "",
282 node->schema->name, DO_FORMAT ? " " : "");
283 }
284
285 return LY_SUCCESS;
286}
287
288/**
289 * @brief More generic alternative to json_print_member() to print some special cases of the member names.
290 *
291 * @param[in] ctx JSON printer context.
292 * @param[in] parent Parent node to compare modules deciding if the prefix is printed.
293 * @param[in] format Format to decide how to process the @p prefix.
294 * @param[in] prefix Prefix structure to provide prefix string if prefix to print.
295 * @param[in] name Name of the memeber to print.
296 * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
297 * @return LY_ERR value.
298 */
299static LY_ERR
300json_print_member2(struct jsonpr_ctx *ctx, const struct lyd_node *parent, LYD_FORMAT format, const struct ly_prefix *prefix, const char *name, int is_attr)
301{
302 const char *module_name = NULL;
303
304 PRINT_COMMA;
305
306 /* determine prefix string */
307 if (prefix) {
308 const struct lys_module *mod;
309
310 switch (format) {
311 case LYD_JSON:
312 module_name = prefix->module_name;
313 break;
314 case LYD_XML:
315 mod = ly_ctx_get_module_implemented_ns(ctx->ctx, prefix->module_ns);
316 if (mod) {
317 module_name = mod->name;
318 }
319 break;
320 case LYD_SCHEMA:
321 case LYD_LYB:
322 /* cannot be created */
323 LOGINT_RET(ctx->ctx);
324 }
325 }
326
327 /* print the member */
328 if (module_name && (!parent || node_prefix(parent) != module_name)) {
329 ly_print(ctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "", module_name, name, DO_FORMAT ? " " : "");
330 } else {
331 ly_print(ctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", name, DO_FORMAT ? " " : "");
332 }
333
334 return LY_SUCCESS;
335}
336
337/**
338 * @brief Print data value.
339 *
340 * @param[in] ctx JSON printer context.
341 * @param[in] val Data value to be printed.
342 * @return LY_ERR value.
343 */
344static LY_ERR
345json_print_value(struct jsonpr_ctx *ctx, const struct lyd_value *val)
346{
347 int dynamic = 0;
348 const char *value = val->realtype->plugin->print(val, LYD_JSON, json_print_get_prefix, NULL, &dynamic);
349
350 /* leafref is not supported */
351 switch (val->realtype->basetype) {
352 case LY_TYPE_BINARY:
353 case LY_TYPE_STRING:
354 case LY_TYPE_BITS:
355 case LY_TYPE_ENUM:
356 case LY_TYPE_INST:
357 case LY_TYPE_INT64:
358 case LY_TYPE_UINT64:
359 case LY_TYPE_DEC64:
360 case LY_TYPE_IDENT:
361 json_print_string(ctx->out, value);
362 break;
363
364 case LY_TYPE_INT8:
365 case LY_TYPE_INT16:
366 case LY_TYPE_INT32:
367 case LY_TYPE_UINT8:
368 case LY_TYPE_UINT16:
369 case LY_TYPE_UINT32:
370 case LY_TYPE_BOOL:
371 ly_print(ctx->out, "%s", value[0] ? value : "null");
372 break;
373
374 case LY_TYPE_EMPTY:
375 ly_print(ctx->out, "[null]");
376 break;
377
378 default:
379 /* error */
380 LOGINT_RET(ctx->ctx);
381 }
382
383 if (dynamic) {
384 free((char*)value);
385 }
386
387 return LY_SUCCESS;
388}
389
390/**
391 * @brief Print all the attributes of the opaq node.
392 *
393 * @param[in] ctx JSON printer context.
394 * @param[in] node Opaq node where the attributes are placed.
395 * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
396 * @return LY_ERR value.
397 */
398static LY_ERR
399json_print_attribute(struct jsonpr_ctx *ctx, const struct lyd_node_opaq *node, const struct lys_module *wdmod)
400{
401 struct lyd_attr *attr;
402
403 if (wdmod) {
404 ly_print(ctx->out, "%*s\"%s:default\":\"true\"", INDENT, wdmod->name);
405 LEVEL_PRINTED;
406 }
407
408 for (attr = node->attr; attr; attr = attr->next) {
409 PRINT_COMMA;
410 json_print_member2(ctx, (struct lyd_node*)node, attr->format, &attr->prefix, attr->name, 0);
411
412 if (attr->hint & (LYD_NODE_OPAQ_ISBOOLEAN | LYD_NODE_OPAQ_ISNUMBER)) {
413 ly_print(ctx->out, "%s", attr->value[0] ? attr->value : "null");
414 } else if (attr->hint & LYD_NODE_OPAQ_ISEMPTY) {
415 ly_print(ctx->out, "[null]");
416 } else {
417 json_print_string(ctx->out, attr->value);
418 }
419 LEVEL_PRINTED;
420 }
421
422 return LY_SUCCESS;
423}
424
425/**
426 * @brief Print all the metadata of the node.
427 *
428 * @param[in] ctx JSON printer context.
429 * @param[in] node Node where the metadata are placed.
430 * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
431 * @return LY_ERR value.
432 */
433static LY_ERR
434json_print_metadata(struct jsonpr_ctx *ctx, const struct lyd_node *node, const struct lys_module *wdmod)
435{
436 struct lyd_meta *meta;
437
438 if (wdmod) {
439 ly_print(ctx->out, "%*s\"%s:default\":\"true\"", INDENT, wdmod->name);
440 LEVEL_PRINTED;
441 }
442
443 for (meta = node->meta; meta; meta = meta->next) {
444 PRINT_COMMA;
445 ly_print(ctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
446 LY_CHECK_RET(json_print_value(ctx, &meta->value));
447 LEVEL_PRINTED;
448 }
449
450 return LY_SUCCESS;
451}
452
453/**
454 * @brief Print attributes/metadata of the given @p node. Accepts both regular as well as opaq nodes.
455 *
456 * @param[in] ctx JSON printer context.
457 * @param[in] node Data node where the attributes/metadata are placed.
458 * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
459 * @return LY_ERR value.
460 */
461static LY_ERR
462json_print_attributes(struct jsonpr_ctx *ctx, const struct lyd_node *node, int inner)
463{
464 const struct lys_module *wdmod = NULL;
465
466 if ((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
467 /* we have implicit OR explicit default node */
468 /* get with-defaults module */
469 wdmod = ly_ctx_get_module_implemented(LYD_NODE_CTX(node), "ietf-netconf-with-defaults");
470 }
471
472 if (node->schema && node->meta) {
473 if (inner) {
474 LY_CHECK_RET(json_print_member2(ctx, NULL, LYD_JSON, NULL, "", 1));
475 } else {
476 LY_CHECK_RET(json_print_member(ctx, node, 1));
477 }
478 ly_print(ctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
479 LEVEL_INC;
480 LY_CHECK_RET(json_print_metadata(ctx, node, wdmod));
481 LEVEL_DEC;
482 ly_print(ctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
483 LEVEL_PRINTED;
484 } else if (!node->schema && ((struct lyd_node_opaq*)node)->attr) {
485 if (inner) {
486 LY_CHECK_RET(json_print_member2(ctx, NULL, LYD_JSON, NULL, "", 1));
487 } else {
488 LY_CHECK_RET(json_print_member2(ctx, node, ((struct lyd_node_opaq*)node)->format,
489 &((struct lyd_node_opaq*)node)->prefix, ((struct lyd_node_opaq*)node)->name, 1));
490 }
491 ly_print(ctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
492 LEVEL_INC;
493 LY_CHECK_RET(json_print_attribute(ctx, (struct lyd_node_opaq*)node, wdmod));
494 LEVEL_DEC;
495 ly_print(ctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
496 LEVEL_PRINTED;
497 }
498
499 return LY_SUCCESS;
500}
501
502/**
503 * @brief Print leaf data node including its metadata.
504 *
505 * @param[in] ctx JSON printer context.
506 * @param[in] node Data node to print.
507 * @return LY_ERR value.
508 */
509static LY_ERR
510json_print_leaf(struct jsonpr_ctx *ctx, const struct lyd_node *node)
511{
512 LY_CHECK_RET(json_print_member(ctx, node, 0));
513 LY_CHECK_RET(json_print_value(ctx, &((const struct lyd_node_term*)node)->value));
514 LEVEL_PRINTED;
515
516 /* print attributes as sibling */
517 json_print_attributes(ctx, node, 0);
518
519 return LY_SUCCESS;
520}
521
522/**
523 * @brief Print anydata data node including its metadata.
524 *
525 * @param[in] ctx JSON printer context.
526 * @param[in] any Anydata node to print.
527 * @return LY_ERR value.
528 */
529static LY_ERR
530json_print_anydata(struct jsonpr_ctx *ctx, struct lyd_node_any *any)
531{
532 LY_ERR ret = LY_SUCCESS;
533 struct lyd_node *iter;
534 int prev_opts, prev_lo;
535
536 if (!any->value.tree) {
537 /* no content */
538 return LY_SUCCESS;
539 }
540
541 if (any->value_type == LYD_ANYDATA_LYB) {
542 int parser_options = LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT;
543
544 /* turn logging off */
545 prev_lo = ly_log_options(0);
546
547 /* try to parse it into a data tree */
548 if (lyd_parse_data_mem(ctx->ctx, any->value.mem, LYD_LYB, parser_options, 0, &iter) == LY_SUCCESS) {
549 /* successfully parsed */
550 free(any->value.mem);
551 any->value.tree = iter;
552 any->value_type = LYD_ANYDATA_DATATREE;
553 }
554
555 /* turn loggin on again */
556 ly_log_options(prev_lo);
557 }
558
559 switch (any->value_type) {
560 case LYD_ANYDATA_DATATREE:
561 /* close opening tag and print data */
562 prev_opts = ctx->options;
563 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
564
565 LY_LIST_FOR(any->value.tree, iter) {
566 ret = json_print_node(ctx, iter);
567 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
568 }
569
570 ctx->options = prev_opts;
571 break;
572 case LYD_ANYDATA_JSON:
573 /* print without escaping special characters */
574 if (!any->value.str[0]) {
575 return LY_SUCCESS;
576 }
577 ly_print(ctx->out, "%*s%s", INDENT, any->value.str);
578 break;
579 case LYD_ANYDATA_STRING:
580 case LYD_ANYDATA_XML:
581 case LYD_ANYDATA_LYB:
582 /* JSON and LYB format is not supported */
583 LOGWRN(ctx->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
584 return LY_SUCCESS;
585 }
586
587 return LY_SUCCESS;
588}
589
590/**
591 * @brief Print content of a single container/list data node including its metadata.
592 * The envelope specific to container and list are expected to be printed by the caller.
593 *
594 * @param[in] ctx JSON printer context.
595 * @param[in] node Data node to print.
596 * @return LY_ERR value.
597 */
598static LY_ERR
599json_print_inner(struct jsonpr_ctx *ctx, const struct lyd_node *node)
600{
601 struct lyd_node *child;
602 struct lyd_node *children = lyd_node_children(node, 0);
603 int has_content = 0;
604
605 if (node->meta || children) {
606 has_content = 1;
607 } else if (node->schema && (node->schema->nodetype & LYD_NODE_ANY) && ((struct lyd_node_any*)node)->value.tree) {
608 has_content = 1;
609 }
610
611 if (!node->schema || node->schema->nodetype != LYS_LIST) {
612 ly_print(ctx->out, "%s{%s", (is_open_array(ctx, node) && ctx->level_printed >= ctx->level) ? "," : "",
613 (DO_FORMAT && has_content) ? "\n" : "");
614 } else {
615 ly_print(ctx->out, "%s%*s{%s", (is_open_array(ctx, node) && ctx->level_printed >= ctx->level) ? "," : "",
616 INDENT, (DO_FORMAT && has_content) ? "\n" : "");
617 }
618 LEVEL_INC;
619
620 json_print_attributes(ctx, node, 1);
621
622 if (!node->schema || !(node->schema->nodetype & LYS_ANYDATA)) {
623 /* print children */
624 LY_LIST_FOR(children, child) {
625 LY_CHECK_RET(json_print_node(ctx, child));
626 }
627 } else {
628 /* anydata */
629 json_print_anydata(ctx, (struct lyd_node_any *)node);
630 }
631
632
633 LEVEL_DEC;
634 if (DO_FORMAT && has_content) {
635 ly_print(ctx->out, "\n%*s}", INDENT);
636 } else {
637 ly_print(ctx->out, "}");
638 }
639 LEVEL_PRINTED;
640
641 return LY_SUCCESS;
642}
643
644/**
645 * @brief Print container data node including its metadata.
646 *
647 * @param[in] ctx JSON printer context.
648 * @param[in] node Data node to print.
649 * @return LY_ERR value.
650 */
651static int
652json_print_container(struct jsonpr_ctx *ctx, const struct lyd_node *node)
653{
654 LY_CHECK_RET(json_print_member(ctx, node, 0));
655 LY_CHECK_RET(json_print_inner(ctx, node));
656
657 return LY_SUCCESS;
658}
659
660/**
661 * @brief Print single leaf-list or list instance.
662 *
663 * In case of list, metadata are printed inside the list object. For the leaf-list,
664 * metadata are marked in the context for later printing after closing the array next to it using
665 * json_print_metadata_leaflist().
666 *
667 * @param[in] ctx JSON printer context.
668 * @param[in] node Data node to print.
669 * @return LY_ERR value.
670 */
671static LY_ERR
672json_print_leaf_list(struct jsonpr_ctx *ctx, const struct lyd_node *node)
673{
674 if (!is_open_array(ctx, node)) {
675 LY_CHECK_RET(json_print_member(ctx, node, 0));
676 json_print_array_open(ctx, node);
677 } else if (node->schema->nodetype == LYS_LEAFLIST) {
678 ly_print(ctx->out, ",");
679 }
680
681 if (node->schema->nodetype == LYS_LIST) {
682 if (!lyd_node_children(node, 0)) {
683 /* empty, e.g. in case of filter */
684 ly_print(ctx->out, "%s%snull", (ctx->level_printed >= ctx->level) ? "," : "", DO_FORMAT ? " " : "");
685 LEVEL_PRINTED;
686 } else {
687 /* print list's content */
688 LY_CHECK_RET(json_print_inner(ctx, node));
689 }
690 } else {
691 assert(node->schema->nodetype == LYS_LEAFLIST);
692 LY_CHECK_RET(json_print_value(ctx, &((const struct lyd_node_term*)node)->value));
693
694 if (node->meta && !ctx->print_sibling_metadata) {
695 ctx->print_sibling_metadata = node;
696 }
697 }
698
699 if (is_open_array(ctx, node) && (!node->next || node->next->schema != node->schema)) {
700 json_print_array_close(ctx);
701 }
702
703 return LY_SUCCESS;
704}
705
706/**
707 * @brief Print leaf-list's metadata in case they were marked in the last call to json_print_leaf_list().
708 * This function is supposed to be called when the leaf-list array is closed.
709 *
710 * @param[in] ctx JSON printer context.
711 * @return LY_ERR value.
712 */
713static LY_ERR
714json_print_metadata_leaflist(struct jsonpr_ctx *ctx)
715{
716 const struct lyd_node *prev, *node, *iter;
717
718 if (!ctx->print_sibling_metadata) {
719 return LY_SUCCESS;
720 }
721
722 for (node = ctx->print_sibling_metadata, prev = ctx->print_sibling_metadata->prev;
723 prev->next && matching_node(node, prev);
724 node = prev, prev = node->prev) {}
725
726 /* node is the first instance of the leaf-list */
727
728 LY_CHECK_RET(json_print_member(ctx, node, 1));
729 ly_print(ctx->out, "[%s", (DO_FORMAT ? "\n" : ""));
730 LEVEL_INC;
731 LY_LIST_FOR(node, iter) {
732 PRINT_COMMA;
733 if (iter->meta) {
734 ly_print(ctx->out, "%*s%s", INDENT, DO_FORMAT ? "{\n" : "{");
735 LEVEL_INC;
736 LY_CHECK_RET(json_print_metadata(ctx, iter, NULL));
737 LEVEL_DEC;
738 ly_print(ctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
739 } else {
740 ly_print(ctx->out, "null");
741 }
742 LEVEL_PRINTED;
743 if (!matching_node(iter, iter->next)) {
744 break;
745 }
746 }
747 LEVEL_DEC;
748 ly_print(ctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
749 LEVEL_PRINTED;
750
751 return LY_SUCCESS;
752}
753
754/**
755 * @brief Print opaq data node including its attributes.
756 *
757 * @param[in] ctx JSON printer context.
758 * @param[in] node Opaq node to print.
759 * @return LY_ERR value.
760 */
761static LY_ERR
762json_print_opaq(struct jsonpr_ctx *ctx, const struct lyd_node_opaq *node)
763{
764 int first = 1, last = 1;
765
766 if (node->hint & LYD_NODE_OPAQ_ISLIST) {
767 const struct lyd_node_opaq *prev = (const struct lyd_node_opaq*)node->prev;
768 const struct lyd_node_opaq *next = (const struct lyd_node_opaq*)node->next;
769 if (prev->next && matching_node((const struct lyd_node*)prev, (const struct lyd_node*)node)) {
770 first = 0;
771 }
772 if (next && matching_node((const struct lyd_node*)node, (const struct lyd_node*)next)) {
773 last = 0;
774 }
775 }
776
777 if (first) {
778 LY_CHECK_RET(json_print_member2(ctx, node->parent, node->format, &node->prefix, node->name, 0));
779
780 if (node->hint & LYD_NODE_OPAQ_ISLIST) {
781 json_print_array_open(ctx, (struct lyd_node*)node);
782 LEVEL_INC;
783 }
784 }
785 if (node->child || (node->hint & LYD_NODE_OPAQ_ISLIST)) {
786 LY_CHECK_RET(json_print_inner(ctx, (struct lyd_node*)node));
787 LEVEL_PRINTED;
788 } else {
789 if (node->hint & LYD_VALUE_PARSE_ISEMPTY) {
790 ly_print(ctx->out, "[null]");
791 } else if (node->hint & (LYD_VALUE_PARSE_ISBOOLEAN | LYD_VALUE_PARSE_ISNUMBER)) {
792 ly_print(ctx->out, "%s", node->value);
793 } else {
794 /* string */
795 ly_print(ctx->out, "\"%s\"", node->value);
796 }
797 LEVEL_PRINTED;
798
799 /* attributes */
800 json_print_attributes(ctx, (const struct lyd_node*)node, 0);
801
802 }
803 if (last && (node->hint & LYD_NODE_OPAQ_ISLIST)) {
804 json_print_array_close(ctx);
805 LEVEL_DEC;
806 LEVEL_PRINTED;
807 }
808
809 return LY_SUCCESS;
810}
811
812/**
813 * @brief Print all the types of data node including its metadata.
814 *
815 * @param[in] ctx JSON printer context.
816 * @param[in] node Data node to print.
817 * @return LY_ERR value.
818 */
819static LY_ERR
820json_print_node(struct jsonpr_ctx *ctx, const struct lyd_node *node)
821{
822 if (!ly_should_print(node, ctx->options)) {
823 if (is_open_array(ctx, node) && (!node->next || node->next->schema != node->schema)) {
824 json_print_array_close(ctx);
825 }
826 return LY_SUCCESS;
827 }
828
829 if (!node->schema) {
830 LY_CHECK_RET(json_print_opaq(ctx, (const struct lyd_node_opaq *)node));
831 } else {
832 switch (node->schema->nodetype) {
833 case LYS_RPC:
834 case LYS_ACTION:
835 case LYS_NOTIF:
836 case LYS_CONTAINER:
837 LY_CHECK_RET(json_print_container(ctx, node));
838 break;
839 case LYS_LEAF:
840 LY_CHECK_RET(json_print_leaf(ctx, node));
841 break;
842 case LYS_LEAFLIST:
843 case LYS_LIST:
844 LY_CHECK_RET(json_print_leaf_list(ctx, node));
845 break;
846 case LYS_ANYXML:
847 case LYS_ANYDATA:
848 LY_CHECK_RET(json_print_container(ctx, node));
849 break;
850 default:
851 LOGINT(node->schema->module->ctx);
852 return EXIT_FAILURE;
853 }
854 }
855
856 ctx->level_printed = ctx->level;
857
858 if (ctx->print_sibling_metadata && !matching_node(node->next, ctx->print_sibling_metadata)) {
859 json_print_metadata_leaflist(ctx);
860 ctx->print_sibling_metadata = NULL;
861 }
862
863 return LY_SUCCESS;
864}
865
866LY_ERR
867json_print_data(struct ly_out *out, const struct lyd_node *root, int options)
868{
869 const struct lyd_node *node;
870 struct jsonpr_ctx ctx = {0};
871 const char *delimiter = (options & LYD_PRINT_FORMAT) ? "\n" : "";
872
873 ctx.out = out;
874 ctx.level = 1;
875 ctx.level_printed = 0;
876 ctx.options = options;
877 ctx.ctx = LYD_NODE_CTX(root);
878
879 /* start */
880 ly_print(ctx.out, "{%s", delimiter);
881
882 /* content */
883 LY_LIST_FOR(root, node) {
884 LY_CHECK_RET(json_print_node(&ctx, node));
885 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
886 break;
887 }
888 }
889
890 /* end */
891 ly_print(out, "%s}%s", delimiter, delimiter);
892
893 assert(!ctx.open.count);
894 ly_set_erase(&ctx.open, NULL);
895
896 ly_print_flush(out);
897 return LY_SUCCESS;
898}