blob: 4c53f0f50108cc9a9872fa6c54155563ed74dcec [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>
Radek Krejci47fab892020-11-05 17:02:41 +010016#include <stdint.h>
Radek Krejci5536d282020-08-04 23:27:44 +020017#include <stdlib.h>
Radek Krejci5536d282020-08-04 23:27:44 +020018
Radek Krejci13a57b62019-07-19 13:04:09 +020019#include "common.h"
Radek Krejci47fab892020-11-05 17:02:41 +010020#include "context.h"
Radek Krejci5536d282020-08-04 23:27:44 +020021#include "log.h"
Radek Krejci47fab892020-11-05 17:02:41 +010022#include "out.h"
Michal Vaskoafac7822020-10-20 14:22:26 +020023#include "out_internal.h"
Radek Krejci5536d282020-08-04 23:27:44 +020024#include "parser_data.h"
25#include "plugins_types.h"
26#include "printer_data.h"
27#include "printer_internal.h"
28#include "set.h"
29#include "tree.h"
30#include "tree_data.h"
Radek Krejci13a57b62019-07-19 13:04:09 +020031#include "tree_schema.h"
32
33/**
Radek Krejci5536d282020-08-04 23:27:44 +020034 * @brief JSON printer context.
35 */
36struct jsonpr_ctx {
Radek Krejci1deb5be2020-08-26 16:43:36 +020037 struct ly_out *out; /**< output specification */
Michal Vaskoaa22f422021-12-02 10:48:32 +010038 const struct lyd_node *root; /**< root node of the subtree being printed */
Michal Vasko26743a22022-03-29 14:48:10 +020039 const struct lyd_node *parent; /**< parent of the node being printed */
Radek Krejci1deb5be2020-08-26 16:43:36 +020040 uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
41 uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
42 const struct ly_ctx *ctx; /**< libyang context */
Radek Krejci5536d282020-08-04 23:27:44 +020043
Radek Krejci1deb5be2020-08-26 16:43:36 +020044 uint16_t level_printed; /* level where some data were already printed */
Michal Vaskoaa22f422021-12-02 10:48:32 +010045 struct ly_set open; /* currently open array(s) */
Radek Krejci5536d282020-08-04 23:27:44 +020046 const struct lyd_node *print_sibling_metadata;
47};
48
49/**
50 * @brief Mark that something was already written in the current level,
51 * used to decide if a comma is expected between the items
52 */
Michal Vasko61ad1ff2022-02-10 15:48:39 +010053#define LEVEL_PRINTED pctx->level_printed = pctx->level
Radek Krejci5536d282020-08-04 23:27:44 +020054
55#define PRINT_COMMA \
Michal Vasko61ad1ff2022-02-10 15:48:39 +010056 if (pctx->level_printed >= pctx->level) { \
57 ly_print_(pctx->out, ",%s", (DO_FORMAT ? "\n" : "")); \
Radek Krejci5536d282020-08-04 23:27:44 +020058 }
59
Michal Vasko61ad1ff2022-02-10 15:48:39 +010060static LY_ERR json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node);
Radek Krejci5536d282020-08-04 23:27:44 +020061
62/**
Radek Krejci5536d282020-08-04 23:27:44 +020063 * Compare 2 nodes, despite it is regular data node or an opaq node, and
64 * decide if they corresponds to the same schema node.
65 *
66 * TODO: rewrite lyd_compare_single and use it instead of this
67 *
68 * @return 1 - matching nodes, 0 - non-matching nodes
69 */
70static int
71matching_node(const struct lyd_node *node1, const struct lyd_node *node2)
72{
73 assert(node1 || node2);
74
75 if (!node1 || !node2) {
76 return 0;
77 } else if (node1->schema != node2->schema) {
78 return 0;
79 }
80 if (!node1->schema) {
81 /* compare node names */
Michal Vasko22df3f02020-08-24 13:29:22 +020082 struct lyd_node_opaq *onode1 = (struct lyd_node_opaq *)node1;
83 struct lyd_node_opaq *onode2 = (struct lyd_node_opaq *)node2;
Michal Vaskoad92b672020-11-12 13:11:31 +010084 if ((onode1->name.name != onode2->name.name) || (onode1->name.prefix != onode2->name.prefix)) {
Radek Krejci5536d282020-08-04 23:27:44 +020085 return 0;
86 }
87 }
88
89 return 1;
90}
91
92/**
93 * @brief Open the JSON array ('[') for the specified @p node
94 *
95 * @param[in] ctx JSON printer context.
96 * @param[in] node First node of the array.
Michal Vaskob0099a92020-08-31 14:55:23 +020097 * @return LY_ERR value.
Radek Krejci5536d282020-08-04 23:27:44 +020098 */
Michal Vaskob0099a92020-08-31 14:55:23 +020099static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100100json_print_array_open(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200101{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100102 ly_print_(pctx->out, "[%s", DO_FORMAT ? "\n" : "");
103 LY_CHECK_RET(ly_set_add(&pctx->open, (void *)node, 0, NULL));
Radek Krejci5536d282020-08-04 23:27:44 +0200104 LEVEL_INC;
Michal Vaskob0099a92020-08-31 14:55:23 +0200105
106 return LY_SUCCESS;
Radek Krejci5536d282020-08-04 23:27:44 +0200107}
108
109/**
110 * @brief Get know if the array for the provided @p node is currently open.
111 *
112 * @param[in] ctx JSON printer context.
113 * @param[in] node Data node to check.
114 * @return 1 in case the printer is currently in the array belonging to the provided @p node.
115 * @return 0 in case the provided @p node is not connected with the currently open array (or there is no open array).
116 */
117static int
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100118is_open_array(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200119{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100120 if (pctx->open.count && matching_node(node, pctx->open.dnodes[pctx->open.count - 1])) {
Radek Krejci5536d282020-08-04 23:27:44 +0200121 return 1;
122 } else {
123 return 0;
124 }
125}
126
127/**
128 * @brief Close the most inner JSON array.
129 *
130 * @param[in] ctx JSON printer context.
131 */
132static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100133json_print_array_close(struct jsonpr_ctx *pctx)
Radek Krejci5536d282020-08-04 23:27:44 +0200134{
Radek Krejci5536d282020-08-04 23:27:44 +0200135 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100136 ly_set_rm_index(&pctx->open, pctx->open.count - 1, NULL);
137 ly_print_(pctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200138}
139
140/**
141 * @brief Get the node's module name to use as the @p node prefix in JSON.
Radek Krejci31bc3f52021-04-26 11:09:58 +0200142 *
Radek Krejci5536d282020-08-04 23:27:44 +0200143 * @param[in] node Node to process.
144 * @return The name of the module where the @p node belongs, it can be NULL in case the module name
145 * cannot be determined (source format is XML and the refered namespace is unknown/not implemented in the current context).
146 */
147static const char *
148node_prefix(const struct lyd_node *node)
149{
150 if (node->schema) {
151 return node->schema->module->name;
152 } else {
Michal Vasko22df3f02020-08-24 13:29:22 +0200153 struct lyd_node_opaq *onode = (struct lyd_node_opaq *)node;
Radek Krejci5536d282020-08-04 23:27:44 +0200154 const struct lys_module *mod;
155
156 switch (onode->format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200157 case LY_VALUE_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100158 return onode->name.module_name;
Radek Krejci8df109d2021-04-23 12:19:08 +0200159 case LY_VALUE_XML:
Michal Vaskoad92b672020-11-12 13:11:31 +0100160 mod = ly_ctx_get_module_implemented_ns(onode->ctx, onode->name.module_ns);
Radek Krejci5536d282020-08-04 23:27:44 +0200161 if (!mod) {
162 return NULL;
163 }
164 return mod->name;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100165 default:
Radek Krejci5536d282020-08-04 23:27:44 +0200166 /* cannot be created */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200167 LOGINT(LYD_CTX(node));
Radek Krejci5536d282020-08-04 23:27:44 +0200168 }
169 }
170
171 return NULL;
172}
173
174/**
175 * @brief Compare 2 nodes if the belongs to the same module (if they come from the same namespace)
176 *
177 * Accepts both regulard a well as opaq nodes.
178 *
179 * @param[in] node1 The first node to compare.
180 * @param[in] node2 The second node to compare.
181 * @return 0 in case the nodes' modules are the same
182 * @return 1 in case the nodes belongs to different modules
183 */
184int
185json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
186{
187 assert(node1 || node2);
188
189 if (!node1 || !node2) {
190 return 1;
191 } else if (node1->schema && node2->schema) {
192 if (node1->schema->module == node2->schema->module) {
193 /* belongs to the same module */
194 return 0;
195 } else {
196 /* different modules */
197 return 1;
198 }
199 } else {
200 const char *pref1 = node_prefix(node1);
201 const char *pref2 = node_prefix(node2);
Michal Vasko69730152020-10-09 16:30:07 +0200202 if ((pref1 && pref2) && (pref1 == pref2)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200203 return 0;
204 } else {
205 return 1;
206 }
207 }
208}
209
210/**
211 * @brief Print the @p text as JSON string - encode special characters and add quotation around the string.
212 *
213 * @param[in] out The output handler.
214 * @param[in] text The string to print.
Michal Vasko5233e962020-08-14 14:26:20 +0200215 * @return LY_ERR value.
Radek Krejci5536d282020-08-04 23:27:44 +0200216 */
Michal Vasko5233e962020-08-14 14:26:20 +0200217static LY_ERR
Radek Krejci5536d282020-08-04 23:27:44 +0200218json_print_string(struct ly_out *out, const char *text)
219{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200220 uint64_t i, n;
Radek Krejci5536d282020-08-04 23:27:44 +0200221
222 if (!text) {
Michal Vasko5233e962020-08-14 14:26:20 +0200223 return LY_SUCCESS;
Radek Krejci5536d282020-08-04 23:27:44 +0200224 }
225
Michal Vasko5233e962020-08-14 14:26:20 +0200226 ly_write_(out, "\"", 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200227 for (i = n = 0; text[i]; i++) {
228 const unsigned char ascii = text[i];
229 if (ascii < 0x20) {
230 /* control character */
Michal Vasko5233e962020-08-14 14:26:20 +0200231 ly_print_(out, "\\u%.4X", ascii);
Radek Krejci5536d282020-08-04 23:27:44 +0200232 } else {
233 switch (ascii) {
234 case '"':
Michal Vasko5233e962020-08-14 14:26:20 +0200235 ly_print_(out, "\\\"");
Radek Krejci5536d282020-08-04 23:27:44 +0200236 break;
237 case '\\':
Michal Vasko5233e962020-08-14 14:26:20 +0200238 ly_print_(out, "\\\\");
Radek Krejci5536d282020-08-04 23:27:44 +0200239 break;
240 default:
Michal Vasko5233e962020-08-14 14:26:20 +0200241 ly_write_(out, &text[i], 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200242 n++;
243 }
244 }
245 }
Michal Vasko5233e962020-08-14 14:26:20 +0200246 ly_write_(out, "\"", 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200247
Michal Vasko5233e962020-08-14 14:26:20 +0200248 return LY_SUCCESS;
Radek Krejci5536d282020-08-04 23:27:44 +0200249}
250
251/**
252 * @brief Print JSON object's member name, ending by ':'. It resolves if the prefix is supposed to be printed.
253 *
254 * @param[in] ctx JSON printer context.
255 * @param[in] node The data node being printed.
256 * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
257 * @return LY_ERR value.
258 */
259static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100260json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr)
Radek Krejci5536d282020-08-04 23:27:44 +0200261{
262 PRINT_COMMA;
Michal Vasko26743a22022-03-29 14:48:10 +0200263 if ((LEVEL == 1) || json_nscmp(node, pctx->parent)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200264 /* print "namespace" */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100265 ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "",
Michal Vasko69730152020-10-09 16:30:07 +0200266 node_prefix(node), node->schema->name, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200267 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100268 ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", node->schema->name, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200269 }
270
271 return LY_SUCCESS;
272}
273
274/**
275 * @brief More generic alternative to json_print_member() to print some special cases of the member names.
276 *
277 * @param[in] ctx JSON printer context.
278 * @param[in] parent Parent node to compare modules deciding if the prefix is printed.
279 * @param[in] format Format to decide how to process the @p prefix.
Michal Vaskoad92b672020-11-12 13:11:31 +0100280 * @param[in] name Name structure to provide name and prefix to print. If NULL, only "" name is printed.
Radek Krejci5536d282020-08-04 23:27:44 +0200281 * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
282 * @return LY_ERR value.
283 */
284static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100285json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VALUE_FORMAT format,
Michal Vaskoad92b672020-11-12 13:11:31 +0100286 const struct ly_opaq_name *name, ly_bool is_attr)
Radek Krejci5536d282020-08-04 23:27:44 +0200287{
Michal Vaskoad92b672020-11-12 13:11:31 +0100288 const char *module_name = NULL, *name_str;
Radek Krejci5536d282020-08-04 23:27:44 +0200289
290 PRINT_COMMA;
291
292 /* determine prefix string */
Michal Vaskoad92b672020-11-12 13:11:31 +0100293 if (name) {
Radek Krejci5536d282020-08-04 23:27:44 +0200294 const struct lys_module *mod;
295
296 switch (format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200297 case LY_VALUE_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100298 module_name = name->module_name;
Radek Krejci5536d282020-08-04 23:27:44 +0200299 break;
Radek Krejci8df109d2021-04-23 12:19:08 +0200300 case LY_VALUE_XML:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100301 mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
Radek Krejci5536d282020-08-04 23:27:44 +0200302 if (mod) {
303 module_name = mod->name;
304 }
305 break;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100306 default:
Radek Krejci5536d282020-08-04 23:27:44 +0200307 /* cannot be created */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100308 LOGINT_RET(pctx->ctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200309 }
Michal Vaskoad92b672020-11-12 13:11:31 +0100310
311 name_str = name->name;
312 } else {
313 name_str = "";
Radek Krejci5536d282020-08-04 23:27:44 +0200314 }
315
316 /* print the member */
Michal Vasko69730152020-10-09 16:30:07 +0200317 if (module_name && (!parent || (node_prefix(parent) != module_name))) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100318 ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "", module_name, name_str, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200319 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100320 ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", name_str, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200321 }
322
323 return LY_SUCCESS;
324}
325
326/**
327 * @brief Print data value.
328 *
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100329 * @param[in] pctx JSON printer context.
330 * @param[in] ctx Context used to print the value.
Radek Krejci5536d282020-08-04 23:27:44 +0200331 * @param[in] val Data value to be printed.
332 * @return LY_ERR value.
333 */
334static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100335json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val)
Radek Krejci5536d282020-08-04 23:27:44 +0200336{
aPiecek0f6bf3e2021-08-25 15:47:49 +0200337 ly_bool dynamic;
Michal Vasko183b9112021-11-04 16:02:24 +0100338 LY_DATA_TYPE basetype;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100339 const char *value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
Radek Krejci5536d282020-08-04 23:27:44 +0200340
Michal Vasko183b9112021-11-04 16:02:24 +0100341 basetype = val->realtype->basetype;
342
343print_val:
Radek Krejci5536d282020-08-04 23:27:44 +0200344 /* leafref is not supported */
Michal Vasko183b9112021-11-04 16:02:24 +0100345 switch (basetype) {
346 case LY_TYPE_UNION:
347 /* use the resolved type */
348 basetype = val->subvalue->value.realtype->basetype;
349 goto print_val;
350
Radek Krejci5536d282020-08-04 23:27:44 +0200351 case LY_TYPE_BINARY:
352 case LY_TYPE_STRING:
353 case LY_TYPE_BITS:
354 case LY_TYPE_ENUM:
355 case LY_TYPE_INST:
356 case LY_TYPE_INT64:
357 case LY_TYPE_UINT64:
358 case LY_TYPE_DEC64:
359 case LY_TYPE_IDENT:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100360 json_print_string(pctx->out, value);
Radek Krejci5536d282020-08-04 23:27:44 +0200361 break;
362
363 case LY_TYPE_INT8:
364 case LY_TYPE_INT16:
365 case LY_TYPE_INT32:
366 case LY_TYPE_UINT8:
367 case LY_TYPE_UINT16:
368 case LY_TYPE_UINT32:
369 case LY_TYPE_BOOL:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100370 ly_print_(pctx->out, "%s", value[0] ? value : "null");
Radek Krejci5536d282020-08-04 23:27:44 +0200371 break;
372
373 case LY_TYPE_EMPTY:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100374 ly_print_(pctx->out, "[null]");
Radek Krejci5536d282020-08-04 23:27:44 +0200375 break;
376
377 default:
378 /* error */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100379 LOGINT_RET(pctx->ctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200380 }
381
382 if (dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200383 free((char *)value);
Radek Krejci5536d282020-08-04 23:27:44 +0200384 }
385
386 return LY_SUCCESS;
387}
388
389/**
390 * @brief Print all the attributes of the opaq node.
391 *
392 * @param[in] ctx JSON printer context.
393 * @param[in] node Opaq node where the attributes are placed.
394 * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
395 * @return LY_ERR value.
396 */
397static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100398json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node, const struct lys_module *wdmod)
Radek Krejci5536d282020-08-04 23:27:44 +0200399{
400 struct lyd_attr *attr;
401
402 if (wdmod) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100403 ly_print_(pctx->out, "%*s\"%s:default\":\"true\"", INDENT, wdmod->name);
Radek Krejci5536d282020-08-04 23:27:44 +0200404 LEVEL_PRINTED;
405 }
406
407 for (attr = node->attr; attr; attr = attr->next) {
408 PRINT_COMMA;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100409 json_print_member2(pctx, &node->node, attr->format, &attr->name, 0);
Radek Krejci5536d282020-08-04 23:27:44 +0200410
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200411 if (attr->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100412 ly_print_(pctx->out, "%s", attr->value[0] ? attr->value : "null");
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200413 } else if (attr->hints & LYD_VALHINT_EMPTY) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100414 ly_print_(pctx->out, "[null]");
Radek Krejci5536d282020-08-04 23:27:44 +0200415 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100416 json_print_string(pctx->out, attr->value);
Radek Krejci5536d282020-08-04 23:27:44 +0200417 }
418 LEVEL_PRINTED;
419 }
420
421 return LY_SUCCESS;
422}
423
424/**
425 * @brief Print all the metadata of the node.
426 *
427 * @param[in] ctx JSON printer context.
428 * @param[in] node Node where the metadata are placed.
429 * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
430 * @return LY_ERR value.
431 */
432static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100433json_print_metadata(struct jsonpr_ctx *pctx, const struct lyd_node *node, const struct lys_module *wdmod)
Radek Krejci5536d282020-08-04 23:27:44 +0200434{
435 struct lyd_meta *meta;
436
437 if (wdmod) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100438 ly_print_(pctx->out, "%*s\"%s:default\":\"true\"", INDENT, wdmod->name);
Radek Krejci5536d282020-08-04 23:27:44 +0200439 LEVEL_PRINTED;
440 }
441
442 for (meta = node->meta; meta; meta = meta->next) {
443 PRINT_COMMA;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100444 ly_print_(pctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
445 LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value));
Radek Krejci5536d282020-08-04 23:27:44 +0200446 LEVEL_PRINTED;
447 }
448
449 return LY_SUCCESS;
450}
451
452/**
453 * @brief Print attributes/metadata of the given @p node. Accepts both regular as well as opaq nodes.
454 *
455 * @param[in] ctx JSON printer context.
456 * @param[in] node Data node where the attributes/metadata are placed.
Radek Krejci857189e2020-09-01 13:26:36 +0200457 * @param[in] inner Flag if the @p node is an inner node in the tree.
Radek Krejci5536d282020-08-04 23:27:44 +0200458 * @return LY_ERR value.
459 */
460static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100461json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool inner)
Radek Krejci5536d282020-08-04 23:27:44 +0200462{
463 const struct lys_module *wdmod = NULL;
464
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100465 if ((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
Radek Krejci5536d282020-08-04 23:27:44 +0200466 /* we have implicit OR explicit default node */
467 /* get with-defaults module */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200468 wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults");
Radek Krejci5536d282020-08-04 23:27:44 +0200469 }
470
471 if (node->schema && node->meta) {
472 if (inner) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100473 LY_CHECK_RET(json_print_member2(pctx, NULL, LY_VALUE_JSON, NULL, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200474 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100475 LY_CHECK_RET(json_print_member(pctx, node, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200476 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100477 ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
Radek Krejci5536d282020-08-04 23:27:44 +0200478 LEVEL_INC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100479 LY_CHECK_RET(json_print_metadata(pctx, node, wdmod));
Radek Krejci5536d282020-08-04 23:27:44 +0200480 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100481 ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200482 LEVEL_PRINTED;
Michal Vasko22df3f02020-08-24 13:29:22 +0200483 } else if (!node->schema && ((struct lyd_node_opaq *)node)->attr) {
Radek Krejci5536d282020-08-04 23:27:44 +0200484 if (inner) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100485 LY_CHECK_RET(json_print_member2(pctx, NULL, LY_VALUE_JSON, NULL, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200486 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100487 LY_CHECK_RET(json_print_member2(pctx, node, ((struct lyd_node_opaq *)node)->format,
Michal Vaskoad92b672020-11-12 13:11:31 +0100488 &((struct lyd_node_opaq *)node)->name, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200489 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100490 ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
Radek Krejci5536d282020-08-04 23:27:44 +0200491 LEVEL_INC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100492 LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node, wdmod));
Radek Krejci5536d282020-08-04 23:27:44 +0200493 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100494 ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200495 LEVEL_PRINTED;
496 }
497
498 return LY_SUCCESS;
499}
500
501/**
502 * @brief Print leaf data node including its metadata.
503 *
504 * @param[in] ctx JSON printer context.
505 * @param[in] node Data node to print.
506 * @return LY_ERR value.
507 */
508static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100509json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200510{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100511 LY_CHECK_RET(json_print_member(pctx, node, 0));
512 LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
Radek Krejci5536d282020-08-04 23:27:44 +0200513 LEVEL_PRINTED;
514
515 /* print attributes as sibling */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100516 json_print_attributes(pctx, node, 0);
Radek Krejci5536d282020-08-04 23:27:44 +0200517
518 return LY_SUCCESS;
519}
520
521/**
Michal Vaskobbdadda2022-01-06 11:40:10 +0100522 * @brief Print anydata/anyxml content.
Radek Krejci5536d282020-08-04 23:27:44 +0200523 *
524 * @param[in] ctx JSON printer context.
525 * @param[in] any Anydata node to print.
526 * @return LY_ERR value.
527 */
528static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100529json_print_any_content(struct jsonpr_ctx *pctx, struct lyd_node_any *any)
Radek Krejci5536d282020-08-04 23:27:44 +0200530{
531 LY_ERR ret = LY_SUCCESS;
532 struct lyd_node *iter;
Michal Vasko26743a22022-03-29 14:48:10 +0200533 const struct lyd_node *prev_parent;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200534 uint32_t prev_opts, prev_lo;
Radek Krejci5536d282020-08-04 23:27:44 +0200535
Michal Vasko76096ec2022-02-24 16:06:16 +0100536 assert(any->schema->nodetype & LYD_NODE_ANY);
Radek Krejci5536d282020-08-04 23:27:44 +0200537
538 if (any->value_type == LYD_ANYDATA_LYB) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200539 uint32_t parser_options = LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT;
Radek Krejci5536d282020-08-04 23:27:44 +0200540
541 /* turn logging off */
542 prev_lo = ly_log_options(0);
543
544 /* try to parse it into a data tree */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100545 if (lyd_parse_data_mem(pctx->ctx, any->value.mem, LYD_LYB, parser_options, 0, &iter) == LY_SUCCESS) {
Radek Krejci5536d282020-08-04 23:27:44 +0200546 /* successfully parsed */
547 free(any->value.mem);
548 any->value.tree = iter;
549 any->value_type = LYD_ANYDATA_DATATREE;
550 }
551
552 /* turn loggin on again */
553 ly_log_options(prev_lo);
554 }
555
556 switch (any->value_type) {
557 case LYD_ANYDATA_DATATREE:
Michal Vasko76096ec2022-02-24 16:06:16 +0100558 /* print as an object */
559 ly_print_(pctx->out, "{%s", DO_FORMAT ? "\n" : "");
560 LEVEL_INC;
Michal Vaskobbdadda2022-01-06 11:40:10 +0100561
Radek Krejci5536d282020-08-04 23:27:44 +0200562 /* close opening tag and print data */
Michal Vasko26743a22022-03-29 14:48:10 +0200563 prev_parent = pctx->parent;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100564 prev_opts = pctx->options;
Michal Vasko26743a22022-03-29 14:48:10 +0200565 pctx->parent = &any->node;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100566 pctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci5536d282020-08-04 23:27:44 +0200567 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100568 ret = json_print_node(pctx, iter);
Radek Krejci5536d282020-08-04 23:27:44 +0200569 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
570 }
Michal Vasko26743a22022-03-29 14:48:10 +0200571 pctx->parent = prev_parent;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100572 pctx->options = prev_opts;
Michal Vaskobbdadda2022-01-06 11:40:10 +0100573
Michal Vasko76096ec2022-02-24 16:06:16 +0100574 /* terminate the object */
Michal Vasko23b51a82022-03-29 14:22:34 +0200575 LEVEL_DEC;
Michal Vasko76096ec2022-02-24 16:06:16 +0100576 if (DO_FORMAT) {
577 ly_print_(pctx->out, "\n%*s}", INDENT);
578 } else {
579 ly_print_(pctx->out, "}");
Michal Vaskobbdadda2022-01-06 11:40:10 +0100580 }
Radek Krejci5536d282020-08-04 23:27:44 +0200581 break;
582 case LYD_ANYDATA_JSON:
Michal Vasko76096ec2022-02-24 16:06:16 +0100583 if (!any->value.json) {
584 /* no content */
585 if (any->schema->nodetype == LYS_ANYXML) {
586 ly_print_(pctx->out, "null");
587 } else {
588 ly_print_(pctx->out, "{}");
589 }
590 } else {
591 /* print without escaping special characters */
592 ly_print_(pctx->out, "%s", any->value.json);
Radek Krejci5536d282020-08-04 23:27:44 +0200593 }
Radek Krejci5536d282020-08-04 23:27:44 +0200594 break;
595 case LYD_ANYDATA_STRING:
596 case LYD_ANYDATA_XML:
Michal Vasko76096ec2022-02-24 16:06:16 +0100597 if (!any->value.str) {
598 /* no content */
599 if (any->schema->nodetype == LYS_ANYXML) {
600 ly_print_(pctx->out, "null");
601 } else {
602 ly_print_(pctx->out, "{}");
603 }
604 } else {
Michal Vaskobbdadda2022-01-06 11:40:10 +0100605 /* print as a string */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100606 ly_print_(pctx->out, "\"%s\"", any->value.str);
Michal Vaskobbdadda2022-01-06 11:40:10 +0100607 }
Michal Vasko76096ec2022-02-24 16:06:16 +0100608 break;
Radek Krejci5536d282020-08-04 23:27:44 +0200609 case LYD_ANYDATA_LYB:
Michal Vasko76096ec2022-02-24 16:06:16 +0100610 /* LYB format is not supported */
611 LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as JSON.", any->value_type);
Michal Vaskobbdadda2022-01-06 11:40:10 +0100612 break;
Radek Krejci5536d282020-08-04 23:27:44 +0200613 }
614
615 return LY_SUCCESS;
616}
617
618/**
Michal Vasko76096ec2022-02-24 16:06:16 +0100619 * @brief Print content of a single container/list data node including its metadata.
Michal Vaskobbdadda2022-01-06 11:40:10 +0100620 * The envelope specific to nodes are expected to be printed by the caller.
Radek Krejci5536d282020-08-04 23:27:44 +0200621 *
622 * @param[in] ctx JSON printer context.
623 * @param[in] node Data node to print.
624 * @return LY_ERR value.
625 */
626static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100627json_print_inner(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200628{
629 struct lyd_node *child;
Michal Vasko26743a22022-03-29 14:48:10 +0200630 const struct lyd_node *prev_parent;
Michal Vasko23b51a82022-03-29 14:22:34 +0200631 struct lyd_node_opaq *opaq = NULL;
Radek Krejci857189e2020-09-01 13:26:36 +0200632 ly_bool has_content = 0;
Radek Krejci5536d282020-08-04 23:27:44 +0200633
Michal Vasko630d9892020-12-08 17:11:08 +0100634 LY_LIST_FOR(lyd_child(node), child) {
Michal Vasko8db584d2022-03-30 13:42:48 +0200635 if (lyd_node_should_print(child, pctx->options)) {
Michal Vasko630d9892020-12-08 17:11:08 +0100636 break;
637 }
638 }
639 if (node->meta || child) {
Radek Krejci5536d282020-08-04 23:27:44 +0200640 has_content = 1;
Radek Krejci5536d282020-08-04 23:27:44 +0200641 }
Michal Vasko23b51a82022-03-29 14:22:34 +0200642 if (!node->schema) {
643 opaq = (struct lyd_node_opaq *)node;
644 }
Radek Krejci5536d282020-08-04 23:27:44 +0200645
Michal Vasko1a85d332021-08-27 10:35:28 +0200646 if ((node->schema && (node->schema->nodetype == LYS_LIST)) ||
Michal Vasko23b51a82022-03-29 14:22:34 +0200647 (opaq && (opaq->hints != LYD_HINT_DATA) && (opaq->hints & LYD_NODEHINT_LIST))) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100648 ly_print_(pctx->out, "%s%*s{%s", (is_open_array(pctx, node) && (pctx->level_printed >= pctx->level)) ?
Michal Vasko1a85d332021-08-27 10:35:28 +0200649 (DO_FORMAT ? ",\n" : ",") : "", INDENT, (DO_FORMAT && has_content) ? "\n" : "");
650 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100651 ly_print_(pctx->out, "%s{%s", (is_open_array(pctx, node) && (pctx->level_printed >= pctx->level)) ? "," : "",
Radek Krejci0f969882020-08-21 16:56:47 +0200652 (DO_FORMAT && has_content) ? "\n" : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200653 }
654 LEVEL_INC;
655
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100656 json_print_attributes(pctx, node, 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200657
Michal Vasko76096ec2022-02-24 16:06:16 +0100658 /* print children */
Michal Vasko26743a22022-03-29 14:48:10 +0200659 prev_parent = pctx->parent;
660 pctx->parent = node;
Michal Vasko76096ec2022-02-24 16:06:16 +0100661 LY_LIST_FOR(lyd_child(node), child) {
662 LY_CHECK_RET(json_print_node(pctx, child));
Radek Krejci5536d282020-08-04 23:27:44 +0200663 }
Michal Vasko26743a22022-03-29 14:48:10 +0200664 pctx->parent = prev_parent;
Radek Krejci5536d282020-08-04 23:27:44 +0200665
Radek Krejci5536d282020-08-04 23:27:44 +0200666 LEVEL_DEC;
667 if (DO_FORMAT && has_content) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100668 ly_print_(pctx->out, "\n%*s}", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200669 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100670 ly_print_(pctx->out, "}");
Radek Krejci5536d282020-08-04 23:27:44 +0200671 }
672 LEVEL_PRINTED;
673
674 return LY_SUCCESS;
675}
676
677/**
678 * @brief Print container data node including its metadata.
679 *
680 * @param[in] ctx JSON printer context.
681 * @param[in] node Data node to print.
682 * @return LY_ERR value.
683 */
684static int
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100685json_print_container(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200686{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100687 LY_CHECK_RET(json_print_member(pctx, node, 0));
688 LY_CHECK_RET(json_print_inner(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200689
690 return LY_SUCCESS;
691}
692
693/**
Michal Vasko76096ec2022-02-24 16:06:16 +0100694 * @brief Print anydata/anyxml data node including its metadata.
Michal Vaskobbdadda2022-01-06 11:40:10 +0100695 *
696 * @param[in] ctx JSON printer context.
697 * @param[in] node Data node to print.
698 * @return LY_ERR value.
699 */
700static int
Michal Vasko76096ec2022-02-24 16:06:16 +0100701json_print_any(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Michal Vaskobbdadda2022-01-06 11:40:10 +0100702{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100703 LY_CHECK_RET(json_print_member(pctx, node, 0));
704 LY_CHECK_RET(json_print_any_content(pctx, (struct lyd_node_any *)node));
Michal Vaskobbdadda2022-01-06 11:40:10 +0100705 LEVEL_PRINTED;
706
707 /* print attributes as sibling */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100708 json_print_attributes(pctx, node, 0);
Michal Vaskobbdadda2022-01-06 11:40:10 +0100709
710 return LY_SUCCESS;
711}
712
713/**
Michal Vaskoaa22f422021-12-02 10:48:32 +0100714 * @brief Check whether a node is the last printed instance of a (leaf-)list.
715 *
716 * @param[in] ctx JSON printer context.
717 * @param[in] node Last printed node.
718 * @return Whether it is the last printed instance or not.
719 */
720static ly_bool
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100721json_print_array_is_last_inst(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Michal Vaskoaa22f422021-12-02 10:48:32 +0100722{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100723 if (!is_open_array(pctx, node)) {
Michal Vasko06217f32021-12-09 09:25:50 +0100724 /* no array open */
725 return 0;
726 }
727
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100728 if ((pctx->root == node) && !(pctx->options & LYD_PRINT_WITHSIBLINGS)) {
Michal Vaskoaa22f422021-12-02 10:48:32 +0100729 /* the only printed instance */
730 return 1;
731 }
732
Michal Vasko06217f32021-12-09 09:25:50 +0100733 if (!node->next || (node->next->schema != node->schema)) {
Michal Vaskoaa22f422021-12-02 10:48:32 +0100734 /* last instance */
735 return 1;
736 }
737
738 return 0;
739}
740
741/**
Radek Krejci5536d282020-08-04 23:27:44 +0200742 * @brief Print single leaf-list or list instance.
743 *
744 * In case of list, metadata are printed inside the list object. For the leaf-list,
745 * metadata are marked in the context for later printing after closing the array next to it using
746 * json_print_metadata_leaflist().
747 *
748 * @param[in] ctx JSON printer context.
749 * @param[in] node Data node to print.
750 * @return LY_ERR value.
751 */
752static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100753json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200754{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100755 if (!is_open_array(pctx, node)) {
756 LY_CHECK_RET(json_print_member(pctx, node, 0));
757 LY_CHECK_RET(json_print_array_open(pctx, node));
Radek Krejciee74a192021-04-09 09:55:34 +0200758 if (node->schema->nodetype == LYS_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100759 ly_print_(pctx->out, "%*s", INDENT);
Radek Krejciee74a192021-04-09 09:55:34 +0200760 }
Radek Krejci5536d282020-08-04 23:27:44 +0200761 } else if (node->schema->nodetype == LYS_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100762 ly_print_(pctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200763 }
764
765 if (node->schema->nodetype == LYS_LIST) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200766 if (!lyd_child(node)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200767 /* empty, e.g. in case of filter */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100768 ly_print_(pctx->out, "%s%snull", (pctx->level_printed >= pctx->level) ? "," : "", DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200769 LEVEL_PRINTED;
770 } else {
771 /* print list's content */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100772 LY_CHECK_RET(json_print_inner(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200773 }
774 } else {
775 assert(node->schema->nodetype == LYS_LEAFLIST);
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100776 LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
Radek Krejci5536d282020-08-04 23:27:44 +0200777
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100778 if (node->meta && !pctx->print_sibling_metadata) {
779 pctx->print_sibling_metadata = node;
Radek Krejci5536d282020-08-04 23:27:44 +0200780 }
781 }
782
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100783 if (json_print_array_is_last_inst(pctx, node)) {
784 json_print_array_close(pctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200785 }
786
787 return LY_SUCCESS;
788}
789
790/**
791 * @brief Print leaf-list's metadata in case they were marked in the last call to json_print_leaf_list().
792 * This function is supposed to be called when the leaf-list array is closed.
793 *
794 * @param[in] ctx JSON printer context.
795 * @return LY_ERR value.
796 */
797static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100798json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
Radek Krejci5536d282020-08-04 23:27:44 +0200799{
800 const struct lyd_node *prev, *node, *iter;
801
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100802 if (!pctx->print_sibling_metadata) {
Radek Krejci5536d282020-08-04 23:27:44 +0200803 return LY_SUCCESS;
804 }
805
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100806 for (node = pctx->print_sibling_metadata, prev = pctx->print_sibling_metadata->prev;
Radek Krejci5536d282020-08-04 23:27:44 +0200807 prev->next && matching_node(node, prev);
808 node = prev, prev = node->prev) {}
809
810 /* node is the first instance of the leaf-list */
811
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100812 LY_CHECK_RET(json_print_member(pctx, node, 1));
813 ly_print_(pctx->out, "[%s", (DO_FORMAT ? "\n" : ""));
Radek Krejci5536d282020-08-04 23:27:44 +0200814 LEVEL_INC;
815 LY_LIST_FOR(node, iter) {
816 PRINT_COMMA;
817 if (iter->meta) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100818 ly_print_(pctx->out, "%*s%s", INDENT, DO_FORMAT ? "{\n" : "{");
Radek Krejci5536d282020-08-04 23:27:44 +0200819 LEVEL_INC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100820 LY_CHECK_RET(json_print_metadata(pctx, iter, NULL));
Radek Krejci5536d282020-08-04 23:27:44 +0200821 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100822 ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200823 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100824 ly_print_(pctx->out, "null");
Radek Krejci5536d282020-08-04 23:27:44 +0200825 }
826 LEVEL_PRINTED;
827 if (!matching_node(iter, iter->next)) {
828 break;
829 }
830 }
831 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100832 ly_print_(pctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200833 LEVEL_PRINTED;
834
835 return LY_SUCCESS;
836}
837
838/**
839 * @brief Print opaq data node including its attributes.
840 *
841 * @param[in] ctx JSON printer context.
842 * @param[in] node Opaq node to print.
843 * @return LY_ERR value.
844 */
845static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100846json_print_opaq(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200847{
Radek Krejci857189e2020-09-01 13:26:36 +0200848 ly_bool first = 1, last = 1;
Michal Vasko23b51a82022-03-29 14:22:34 +0200849 char *ptr;
850 long num;
851
852 if (node->hints == LYD_HINT_DATA) {
853 /* basically, we do not know anything about the node so just do our best */
Michal Vasko26743a22022-03-29 14:48:10 +0200854 LY_CHECK_RET(json_print_member2(pctx, pctx->parent, node->format, &node->name, 0));
Michal Vasko23b51a82022-03-29 14:22:34 +0200855 if (node->child) {
856 LY_CHECK_RET(json_print_inner(pctx, &node->node));
857 LEVEL_PRINTED;
858 } else {
859 if (node->value) {
860 num = strtol(node->value, &ptr, 10);
861 if (!ptr[0] && (ptr != node->value) &&
862 (((num < 0) && (num >= INT32_MIN)) || ((num >= 0) && (num <= UINT32_MAX)))) {
863 ly_print_(pctx->out, "%s", node->value);
864 } else {
865 ly_print_(pctx->out, "\"%s\"", node->value);
866 }
867 } else {
868 ly_print_(pctx->out, "[null]");
869 }
870 LEVEL_PRINTED;
871
872 /* attributes */
873 json_print_attributes(pctx, (const struct lyd_node *)node, 0);
874 }
875 return LY_SUCCESS;
876 }
Radek Krejci5536d282020-08-04 23:27:44 +0200877
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200878 if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100879 if (node->prev->next && matching_node(node->prev, &node->node)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200880 first = 0;
881 }
Michal Vasko9e685082021-01-29 14:49:09 +0100882 if (node->next && matching_node(&node->node, node->next)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200883 last = 0;
884 }
885 }
886
887 if (first) {
Michal Vasko26743a22022-03-29 14:48:10 +0200888 LY_CHECK_RET(json_print_member2(pctx, pctx->parent, node->format, &node->name, 0));
Radek Krejci5536d282020-08-04 23:27:44 +0200889
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200890 if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100891 LY_CHECK_RET(json_print_array_open(pctx, &node->node));
Radek Krejci5536d282020-08-04 23:27:44 +0200892 }
Michal Vasko1a85d332021-08-27 10:35:28 +0200893 if (node->hints & LYD_NODEHINT_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100894 ly_print_(pctx->out, "%*s", INDENT);
Michal Vasko1a85d332021-08-27 10:35:28 +0200895 }
896 } else if (node->hints & LYD_NODEHINT_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100897 ly_print_(pctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200898 }
Michal Vasko1a85d332021-08-27 10:35:28 +0200899 if (node->child || (node->hints & LYD_NODEHINT_LIST)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100900 LY_CHECK_RET(json_print_inner(pctx, &node->node));
Radek Krejci5536d282020-08-04 23:27:44 +0200901 LEVEL_PRINTED;
902 } else {
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200903 if (node->hints & LYD_VALHINT_EMPTY) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100904 ly_print_(pctx->out, "[null]");
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200905 } else if (node->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100906 ly_print_(pctx->out, "%s", node->value);
Radek Krejci5536d282020-08-04 23:27:44 +0200907 } else {
908 /* string */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100909 ly_print_(pctx->out, "\"%s\"", node->value);
Radek Krejci5536d282020-08-04 23:27:44 +0200910 }
911 LEVEL_PRINTED;
912
913 /* attributes */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100914 json_print_attributes(pctx, (const struct lyd_node *)node, 0);
Radek Krejci5536d282020-08-04 23:27:44 +0200915
916 }
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200917 if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100918 json_print_array_close(pctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200919 LEVEL_PRINTED;
920 }
921
922 return LY_SUCCESS;
923}
924
925/**
926 * @brief Print all the types of data node including its metadata.
927 *
928 * @param[in] ctx JSON printer context.
929 * @param[in] node Data node to print.
930 * @return LY_ERR value.
931 */
932static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100933json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200934{
Michal Vasko8db584d2022-03-30 13:42:48 +0200935 if (!lyd_node_should_print(node, pctx->options)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100936 if (json_print_array_is_last_inst(pctx, node)) {
937 json_print_array_close(pctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200938 }
939 return LY_SUCCESS;
940 }
941
942 if (!node->schema) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100943 LY_CHECK_RET(json_print_opaq(pctx, (const struct lyd_node_opaq *)node));
Radek Krejci5536d282020-08-04 23:27:44 +0200944 } else {
945 switch (node->schema->nodetype) {
946 case LYS_RPC:
947 case LYS_ACTION:
948 case LYS_NOTIF:
949 case LYS_CONTAINER:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100950 LY_CHECK_RET(json_print_container(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200951 break;
952 case LYS_LEAF:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100953 LY_CHECK_RET(json_print_leaf(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200954 break;
955 case LYS_LEAFLIST:
956 case LYS_LIST:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100957 LY_CHECK_RET(json_print_leaf_list(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200958 break;
Michal Vasko76096ec2022-02-24 16:06:16 +0100959 case LYS_ANYDATA:
Radek Krejci5536d282020-08-04 23:27:44 +0200960 case LYS_ANYXML:
Michal Vasko76096ec2022-02-24 16:06:16 +0100961 LY_CHECK_RET(json_print_any(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200962 break;
963 default:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100964 LOGINT(pctx->ctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200965 return EXIT_FAILURE;
966 }
967 }
968
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100969 pctx->level_printed = pctx->level;
Radek Krejci5536d282020-08-04 23:27:44 +0200970
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100971 if (pctx->print_sibling_metadata && !matching_node(node->next, pctx->print_sibling_metadata)) {
972 json_print_metadata_leaflist(pctx);
973 pctx->print_sibling_metadata = NULL;
Radek Krejci5536d282020-08-04 23:27:44 +0200974 }
975
976 return LY_SUCCESS;
977}
978
979LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200980json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejci5536d282020-08-04 23:27:44 +0200981{
982 const struct lyd_node *node;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100983 struct jsonpr_ctx pctx = {0};
Radek Krejci52f65552020-09-01 17:03:35 +0200984 const char *delimiter = (options & LYD_PRINT_SHRINK) ? "" : "\n";
Radek Krejci5536d282020-08-04 23:27:44 +0200985
Radek Krejci2e874772020-08-28 16:36:33 +0200986 if (!root) {
987 ly_print_(out, "{}%s", delimiter);
988 ly_print_flush(out);
989 return LY_SUCCESS;
990 }
991
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100992 pctx.out = out;
Michal Vasko26743a22022-03-29 14:48:10 +0200993 pctx.parent = NULL;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100994 pctx.level = 1;
995 pctx.level_printed = 0;
996 pctx.options = options;
997 pctx.ctx = LYD_CTX(root);
Radek Krejci5536d282020-08-04 23:27:44 +0200998
999 /* start */
Michal Vasko61ad1ff2022-02-10 15:48:39 +01001000 ly_print_(pctx.out, "{%s", delimiter);
Radek Krejci5536d282020-08-04 23:27:44 +02001001
1002 /* content */
1003 LY_LIST_FOR(root, node) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +01001004 pctx.root = node;
1005 LY_CHECK_RET(json_print_node(&pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +02001006 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
1007 break;
1008 }
1009 }
1010
1011 /* end */
Michal Vasko5233e962020-08-14 14:26:20 +02001012 ly_print_(out, "%s}%s", delimiter, delimiter);
Radek Krejci5536d282020-08-04 23:27:44 +02001013
Michal Vasko61ad1ff2022-02-10 15:48:39 +01001014 assert(!pctx.open.count);
1015 ly_set_erase(&pctx.open, NULL);
Radek Krejci5536d282020-08-04 23:27:44 +02001016
1017 ly_print_flush(out);
1018 return LY_SUCCESS;
1019}