blob: 31886c96a99f19a549ca740d134d7451afeabc00 [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/**
Michal Vaskob2c61572022-05-20 10:35:33 +020063 * @brief Compare 2 nodes, despite it is regular data node or an opaq node, and
Radek Krejci5536d282020-08-04 23:27:44 +020064 * decide if they corresponds to the same schema node.
65 *
Radek Krejci5536d282020-08-04 23:27:44 +020066 * @return 1 - matching nodes, 0 - non-matching nodes
67 */
68static int
69matching_node(const struct lyd_node *node1, const struct lyd_node *node2)
70{
71 assert(node1 || node2);
72
73 if (!node1 || !node2) {
74 return 0;
75 } else if (node1->schema != node2->schema) {
76 return 0;
77 }
78 if (!node1->schema) {
79 /* compare node names */
Michal Vasko22df3f02020-08-24 13:29:22 +020080 struct lyd_node_opaq *onode1 = (struct lyd_node_opaq *)node1;
81 struct lyd_node_opaq *onode2 = (struct lyd_node_opaq *)node2;
Michal Vasko26bbb272022-08-02 14:54:33 +020082
Michal Vaskoad92b672020-11-12 13:11:31 +010083 if ((onode1->name.name != onode2->name.name) || (onode1->name.prefix != onode2->name.prefix)) {
Radek Krejci5536d282020-08-04 23:27:44 +020084 return 0;
85 }
86 }
87
88 return 1;
89}
90
91/**
92 * @brief Open the JSON array ('[') for the specified @p node
93 *
94 * @param[in] ctx JSON printer context.
95 * @param[in] node First node of the array.
Michal Vaskob0099a92020-08-31 14:55:23 +020096 * @return LY_ERR value.
Radek Krejci5536d282020-08-04 23:27:44 +020097 */
Michal Vaskob0099a92020-08-31 14:55:23 +020098static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +010099json_print_array_open(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200100{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100101 ly_print_(pctx->out, "[%s", DO_FORMAT ? "\n" : "");
102 LY_CHECK_RET(ly_set_add(&pctx->open, (void *)node, 0, NULL));
Radek Krejci5536d282020-08-04 23:27:44 +0200103 LEVEL_INC;
Michal Vaskob0099a92020-08-31 14:55:23 +0200104
105 return LY_SUCCESS;
Radek Krejci5536d282020-08-04 23:27:44 +0200106}
107
108/**
109 * @brief Get know if the array for the provided @p node is currently open.
110 *
111 * @param[in] ctx JSON printer context.
112 * @param[in] node Data node to check.
113 * @return 1 in case the printer is currently in the array belonging to the provided @p node.
114 * @return 0 in case the provided @p node is not connected with the currently open array (or there is no open array).
115 */
116static int
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100117is_open_array(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200118{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100119 if (pctx->open.count && matching_node(node, pctx->open.dnodes[pctx->open.count - 1])) {
Radek Krejci5536d282020-08-04 23:27:44 +0200120 return 1;
121 } else {
122 return 0;
123 }
124}
125
126/**
127 * @brief Close the most inner JSON array.
128 *
129 * @param[in] ctx JSON printer context.
130 */
131static void
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100132json_print_array_close(struct jsonpr_ctx *pctx)
Radek Krejci5536d282020-08-04 23:27:44 +0200133{
Radek Krejci5536d282020-08-04 23:27:44 +0200134 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100135 ly_set_rm_index(&pctx->open, pctx->open.count - 1, NULL);
136 ly_print_(pctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200137}
138
139/**
140 * @brief Get the node's module name to use as the @p node prefix in JSON.
Radek Krejci31bc3f52021-04-26 11:09:58 +0200141 *
Radek Krejci5536d282020-08-04 23:27:44 +0200142 * @param[in] node Node to process.
143 * @return The name of the module where the @p node belongs, it can be NULL in case the module name
144 * cannot be determined (source format is XML and the refered namespace is unknown/not implemented in the current context).
145 */
146static const char *
147node_prefix(const struct lyd_node *node)
148{
149 if (node->schema) {
150 return node->schema->module->name;
151 } else {
Michal Vasko22df3f02020-08-24 13:29:22 +0200152 struct lyd_node_opaq *onode = (struct lyd_node_opaq *)node;
Radek Krejci5536d282020-08-04 23:27:44 +0200153 const struct lys_module *mod;
154
155 switch (onode->format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200156 case LY_VALUE_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100157 return onode->name.module_name;
Radek Krejci8df109d2021-04-23 12:19:08 +0200158 case LY_VALUE_XML:
Michal Vaskoad92b672020-11-12 13:11:31 +0100159 mod = ly_ctx_get_module_implemented_ns(onode->ctx, onode->name.module_ns);
Radek Krejci5536d282020-08-04 23:27:44 +0200160 if (!mod) {
161 return NULL;
162 }
163 return mod->name;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100164 default:
Radek Krejci5536d282020-08-04 23:27:44 +0200165 /* cannot be created */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200166 LOGINT(LYD_CTX(node));
Radek Krejci5536d282020-08-04 23:27:44 +0200167 }
168 }
169
170 return NULL;
171}
172
173/**
174 * @brief Compare 2 nodes if the belongs to the same module (if they come from the same namespace)
175 *
176 * Accepts both regulard a well as opaq nodes.
177 *
178 * @param[in] node1 The first node to compare.
179 * @param[in] node2 The second node to compare.
180 * @return 0 in case the nodes' modules are the same
181 * @return 1 in case the nodes belongs to different modules
182 */
183int
184json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
185{
186 assert(node1 || node2);
187
188 if (!node1 || !node2) {
189 return 1;
190 } else if (node1->schema && node2->schema) {
191 if (node1->schema->module == node2->schema->module) {
192 /* belongs to the same module */
193 return 0;
194 } else {
195 /* different modules */
196 return 1;
197 }
198 } else {
199 const char *pref1 = node_prefix(node1);
200 const char *pref2 = node_prefix(node2);
Michal Vasko26bbb272022-08-02 14:54:33 +0200201
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];
Michal Vasko26bbb272022-08-02 14:54:33 +0200229
Radek Krejci5536d282020-08-04 23:27:44 +0200230 if (ascii < 0x20) {
231 /* control character */
Michal Vasko5233e962020-08-14 14:26:20 +0200232 ly_print_(out, "\\u%.4X", ascii);
Radek Krejci5536d282020-08-04 23:27:44 +0200233 } else {
234 switch (ascii) {
235 case '"':
Michal Vasko5233e962020-08-14 14:26:20 +0200236 ly_print_(out, "\\\"");
Radek Krejci5536d282020-08-04 23:27:44 +0200237 break;
238 case '\\':
Michal Vasko5233e962020-08-14 14:26:20 +0200239 ly_print_(out, "\\\\");
Radek Krejci5536d282020-08-04 23:27:44 +0200240 break;
241 default:
Michal Vasko5233e962020-08-14 14:26:20 +0200242 ly_write_(out, &text[i], 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200243 n++;
244 }
245 }
246 }
Michal Vasko5233e962020-08-14 14:26:20 +0200247 ly_write_(out, "\"", 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200248
Michal Vasko5233e962020-08-14 14:26:20 +0200249 return LY_SUCCESS;
Radek Krejci5536d282020-08-04 23:27:44 +0200250}
251
252/**
253 * @brief Print JSON object's member name, ending by ':'. It resolves if the prefix is supposed to be printed.
254 *
255 * @param[in] ctx JSON printer context.
256 * @param[in] node The data node being printed.
257 * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
258 * @return LY_ERR value.
259 */
260static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100261json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr)
Radek Krejci5536d282020-08-04 23:27:44 +0200262{
263 PRINT_COMMA;
Michal Vasko26743a22022-03-29 14:48:10 +0200264 if ((LEVEL == 1) || json_nscmp(node, pctx->parent)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200265 /* print "namespace" */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100266 ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "",
Michal Vasko69730152020-10-09 16:30:07 +0200267 node_prefix(node), node->schema->name, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200268 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100269 ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", node->schema->name, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200270 }
271
272 return LY_SUCCESS;
273}
274
275/**
276 * @brief More generic alternative to json_print_member() to print some special cases of the member names.
277 *
278 * @param[in] ctx JSON printer context.
279 * @param[in] parent Parent node to compare modules deciding if the prefix is printed.
280 * @param[in] format Format to decide how to process the @p prefix.
Michal Vaskoad92b672020-11-12 13:11:31 +0100281 * @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 +0200282 * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
283 * @return LY_ERR value.
284 */
285static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100286json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VALUE_FORMAT format,
Michal Vaskoad92b672020-11-12 13:11:31 +0100287 const struct ly_opaq_name *name, ly_bool is_attr)
Radek Krejci5536d282020-08-04 23:27:44 +0200288{
Michal Vaskoad92b672020-11-12 13:11:31 +0100289 const char *module_name = NULL, *name_str;
Radek Krejci5536d282020-08-04 23:27:44 +0200290
291 PRINT_COMMA;
292
293 /* determine prefix string */
Michal Vaskoad92b672020-11-12 13:11:31 +0100294 if (name) {
Radek Krejci5536d282020-08-04 23:27:44 +0200295 const struct lys_module *mod;
296
297 switch (format) {
Radek Krejci8df109d2021-04-23 12:19:08 +0200298 case LY_VALUE_JSON:
Michal Vaskoad92b672020-11-12 13:11:31 +0100299 module_name = name->module_name;
Radek Krejci5536d282020-08-04 23:27:44 +0200300 break;
Radek Krejci8df109d2021-04-23 12:19:08 +0200301 case LY_VALUE_XML:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100302 mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
Radek Krejci5536d282020-08-04 23:27:44 +0200303 if (mod) {
304 module_name = mod->name;
305 }
306 break;
Michal Vasko6b5cb2a2020-11-11 19:11:21 +0100307 default:
Radek Krejci5536d282020-08-04 23:27:44 +0200308 /* cannot be created */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100309 LOGINT_RET(pctx->ctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200310 }
Michal Vaskoad92b672020-11-12 13:11:31 +0100311
312 name_str = name->name;
313 } else {
314 name_str = "";
Radek Krejci5536d282020-08-04 23:27:44 +0200315 }
316
317 /* print the member */
Michal Vasko69730152020-10-09 16:30:07 +0200318 if (module_name && (!parent || (node_prefix(parent) != module_name))) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100319 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 +0200320 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100321 ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", name_str, DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200322 }
323
324 return LY_SUCCESS;
325}
326
327/**
328 * @brief Print data value.
329 *
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100330 * @param[in] pctx JSON printer context.
331 * @param[in] ctx Context used to print the value.
Radek Krejci5536d282020-08-04 23:27:44 +0200332 * @param[in] val Data value to be printed.
333 * @return LY_ERR value.
334 */
335static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100336json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val)
Radek Krejci5536d282020-08-04 23:27:44 +0200337{
aPiecek0f6bf3e2021-08-25 15:47:49 +0200338 ly_bool dynamic;
Michal Vasko183b9112021-11-04 16:02:24 +0100339 LY_DATA_TYPE basetype;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100340 const char *value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
Radek Krejci5536d282020-08-04 23:27:44 +0200341
Michal Vasko183b9112021-11-04 16:02:24 +0100342 basetype = val->realtype->basetype;
343
344print_val:
Radek Krejci5536d282020-08-04 23:27:44 +0200345 /* leafref is not supported */
Michal Vasko183b9112021-11-04 16:02:24 +0100346 switch (basetype) {
347 case LY_TYPE_UNION:
348 /* use the resolved type */
349 basetype = val->subvalue->value.realtype->basetype;
350 goto print_val;
351
Radek Krejci5536d282020-08-04 23:27:44 +0200352 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:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100361 json_print_string(pctx->out, value);
Radek Krejci5536d282020-08-04 23:27:44 +0200362 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:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100371 ly_print_(pctx->out, "%s", value[0] ? value : "null");
Radek Krejci5536d282020-08-04 23:27:44 +0200372 break;
373
374 case LY_TYPE_EMPTY:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100375 ly_print_(pctx->out, "[null]");
Radek Krejci5536d282020-08-04 23:27:44 +0200376 break;
377
378 default:
379 /* error */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100380 LOGINT_RET(pctx->ctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200381 }
382
383 if (dynamic) {
Michal Vasko22df3f02020-08-24 13:29:22 +0200384 free((char *)value);
Radek Krejci5536d282020-08-04 23:27:44 +0200385 }
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
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100399json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node, const struct lys_module *wdmod)
Radek Krejci5536d282020-08-04 23:27:44 +0200400{
401 struct lyd_attr *attr;
402
403 if (wdmod) {
Michal Vaskob68c3b42022-05-20 10:36:40 +0200404 ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
Radek Krejci5536d282020-08-04 23:27:44 +0200405 LEVEL_PRINTED;
406 }
407
408 for (attr = node->attr; attr; attr = attr->next) {
409 PRINT_COMMA;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100410 json_print_member2(pctx, &node->node, attr->format, &attr->name, 0);
Radek Krejci5536d282020-08-04 23:27:44 +0200411
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200412 if (attr->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100413 ly_print_(pctx->out, "%s", attr->value[0] ? attr->value : "null");
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200414 } else if (attr->hints & LYD_VALHINT_EMPTY) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100415 ly_print_(pctx->out, "[null]");
Radek Krejci5536d282020-08-04 23:27:44 +0200416 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100417 json_print_string(pctx->out, attr->value);
Radek Krejci5536d282020-08-04 23:27:44 +0200418 }
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
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100434json_print_metadata(struct jsonpr_ctx *pctx, const struct lyd_node *node, const struct lys_module *wdmod)
Radek Krejci5536d282020-08-04 23:27:44 +0200435{
436 struct lyd_meta *meta;
437
438 if (wdmod) {
Michal Vaskob68c3b42022-05-20 10:36:40 +0200439 ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
Radek Krejci5536d282020-08-04 23:27:44 +0200440 LEVEL_PRINTED;
441 }
442
443 for (meta = node->meta; meta; meta = meta->next) {
444 PRINT_COMMA;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100445 ly_print_(pctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
446 LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value));
Radek Krejci5536d282020-08-04 23:27:44 +0200447 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.
Radek Krejci857189e2020-09-01 13:26:36 +0200458 * @param[in] inner Flag if the @p node is an inner node in the tree.
Radek Krejci5536d282020-08-04 23:27:44 +0200459 * @return LY_ERR value.
460 */
461static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100462json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool inner)
Radek Krejci5536d282020-08-04 23:27:44 +0200463{
464 const struct lys_module *wdmod = NULL;
465
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100466 if ((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
Radek Krejci5536d282020-08-04 23:27:44 +0200467 /* we have implicit OR explicit default node */
468 /* get with-defaults module */
Michal Vaskob7be7a82020-08-20 09:09:04 +0200469 wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults");
Radek Krejci5536d282020-08-04 23:27:44 +0200470 }
471
Michal Vaskob68c3b42022-05-20 10:36:40 +0200472 if (node->schema && (node->meta || wdmod)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200473 if (inner) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100474 LY_CHECK_RET(json_print_member2(pctx, NULL, LY_VALUE_JSON, NULL, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200475 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100476 LY_CHECK_RET(json_print_member(pctx, node, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200477 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100478 ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
Radek Krejci5536d282020-08-04 23:27:44 +0200479 LEVEL_INC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100480 LY_CHECK_RET(json_print_metadata(pctx, node, wdmod));
Radek Krejci5536d282020-08-04 23:27:44 +0200481 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100482 ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200483 LEVEL_PRINTED;
Michal Vasko22df3f02020-08-24 13:29:22 +0200484 } else if (!node->schema && ((struct lyd_node_opaq *)node)->attr) {
Radek Krejci5536d282020-08-04 23:27:44 +0200485 if (inner) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100486 LY_CHECK_RET(json_print_member2(pctx, NULL, LY_VALUE_JSON, NULL, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200487 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100488 LY_CHECK_RET(json_print_member2(pctx, node, ((struct lyd_node_opaq *)node)->format,
Michal Vaskoad92b672020-11-12 13:11:31 +0100489 &((struct lyd_node_opaq *)node)->name, 1));
Radek Krejci5536d282020-08-04 23:27:44 +0200490 }
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100491 ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
Radek Krejci5536d282020-08-04 23:27:44 +0200492 LEVEL_INC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100493 LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node, wdmod));
Radek Krejci5536d282020-08-04 23:27:44 +0200494 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100495 ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200496 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
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100510json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200511{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100512 LY_CHECK_RET(json_print_member(pctx, node, 0));
513 LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
Radek Krejci5536d282020-08-04 23:27:44 +0200514 LEVEL_PRINTED;
515
516 /* print attributes as sibling */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100517 json_print_attributes(pctx, node, 0);
Radek Krejci5536d282020-08-04 23:27:44 +0200518
519 return LY_SUCCESS;
520}
521
522/**
Michal Vaskobbdadda2022-01-06 11:40:10 +0100523 * @brief Print anydata/anyxml content.
Radek Krejci5536d282020-08-04 23:27:44 +0200524 *
525 * @param[in] ctx JSON printer context.
526 * @param[in] any Anydata node to print.
527 * @return LY_ERR value.
528 */
529static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100530json_print_any_content(struct jsonpr_ctx *pctx, struct lyd_node_any *any)
Radek Krejci5536d282020-08-04 23:27:44 +0200531{
532 LY_ERR ret = LY_SUCCESS;
533 struct lyd_node *iter;
Michal Vasko26743a22022-03-29 14:48:10 +0200534 const struct lyd_node *prev_parent;
Radek Krejci1deb5be2020-08-26 16:43:36 +0200535 uint32_t prev_opts, prev_lo;
Radek Krejci5536d282020-08-04 23:27:44 +0200536
Michal Vasko76096ec2022-02-24 16:06:16 +0100537 assert(any->schema->nodetype & LYD_NODE_ANY);
Radek Krejci5536d282020-08-04 23:27:44 +0200538
539 if (any->value_type == LYD_ANYDATA_LYB) {
Radek Krejci1deb5be2020-08-26 16:43:36 +0200540 uint32_t parser_options = LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT;
Radek Krejci5536d282020-08-04 23:27:44 +0200541
542 /* turn logging off */
543 prev_lo = ly_log_options(0);
544
545 /* try to parse it into a data tree */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100546 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 +0200547 /* successfully parsed */
548 free(any->value.mem);
549 any->value.tree = iter;
550 any->value_type = LYD_ANYDATA_DATATREE;
551 }
552
553 /* turn loggin on again */
554 ly_log_options(prev_lo);
555 }
556
557 switch (any->value_type) {
558 case LYD_ANYDATA_DATATREE:
Michal Vasko76096ec2022-02-24 16:06:16 +0100559 /* print as an object */
560 ly_print_(pctx->out, "{%s", DO_FORMAT ? "\n" : "");
561 LEVEL_INC;
Michal Vaskobbdadda2022-01-06 11:40:10 +0100562
Radek Krejci5536d282020-08-04 23:27:44 +0200563 /* close opening tag and print data */
Michal Vasko26743a22022-03-29 14:48:10 +0200564 prev_parent = pctx->parent;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100565 prev_opts = pctx->options;
Michal Vasko26743a22022-03-29 14:48:10 +0200566 pctx->parent = &any->node;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100567 pctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci5536d282020-08-04 23:27:44 +0200568 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100569 ret = json_print_node(pctx, iter);
Radek Krejci5536d282020-08-04 23:27:44 +0200570 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
571 }
Michal Vasko26743a22022-03-29 14:48:10 +0200572 pctx->parent = prev_parent;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100573 pctx->options = prev_opts;
Michal Vaskobbdadda2022-01-06 11:40:10 +0100574
Michal Vasko76096ec2022-02-24 16:06:16 +0100575 /* terminate the object */
Michal Vasko23b51a82022-03-29 14:22:34 +0200576 LEVEL_DEC;
Michal Vasko76096ec2022-02-24 16:06:16 +0100577 if (DO_FORMAT) {
578 ly_print_(pctx->out, "\n%*s}", INDENT);
579 } else {
580 ly_print_(pctx->out, "}");
Michal Vaskobbdadda2022-01-06 11:40:10 +0100581 }
Radek Krejci5536d282020-08-04 23:27:44 +0200582 break;
583 case LYD_ANYDATA_JSON:
Michal Vasko76096ec2022-02-24 16:06:16 +0100584 if (!any->value.json) {
585 /* no content */
586 if (any->schema->nodetype == LYS_ANYXML) {
587 ly_print_(pctx->out, "null");
588 } else {
589 ly_print_(pctx->out, "{}");
590 }
591 } else {
592 /* print without escaping special characters */
593 ly_print_(pctx->out, "%s", any->value.json);
Radek Krejci5536d282020-08-04 23:27:44 +0200594 }
Radek Krejci5536d282020-08-04 23:27:44 +0200595 break;
596 case LYD_ANYDATA_STRING:
597 case LYD_ANYDATA_XML:
Michal Vasko76096ec2022-02-24 16:06:16 +0100598 if (!any->value.str) {
599 /* no content */
600 if (any->schema->nodetype == LYS_ANYXML) {
601 ly_print_(pctx->out, "null");
602 } else {
603 ly_print_(pctx->out, "{}");
604 }
605 } else {
Michal Vaskobbdadda2022-01-06 11:40:10 +0100606 /* print as a string */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100607 ly_print_(pctx->out, "\"%s\"", any->value.str);
Michal Vaskobbdadda2022-01-06 11:40:10 +0100608 }
Michal Vasko76096ec2022-02-24 16:06:16 +0100609 break;
Radek Krejci5536d282020-08-04 23:27:44 +0200610 case LYD_ANYDATA_LYB:
Michal Vasko76096ec2022-02-24 16:06:16 +0100611 /* LYB format is not supported */
612 LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as JSON.", any->value_type);
Michal Vaskobbdadda2022-01-06 11:40:10 +0100613 break;
Radek Krejci5536d282020-08-04 23:27:44 +0200614 }
615
616 return LY_SUCCESS;
617}
618
619/**
Michal Vasko76096ec2022-02-24 16:06:16 +0100620 * @brief Print content of a single container/list data node including its metadata.
Michal Vaskobbdadda2022-01-06 11:40:10 +0100621 * The envelope specific to nodes are expected to be printed by the caller.
Radek Krejci5536d282020-08-04 23:27:44 +0200622 *
623 * @param[in] ctx JSON printer context.
624 * @param[in] node Data node to print.
625 * @return LY_ERR value.
626 */
627static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100628json_print_inner(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200629{
630 struct lyd_node *child;
Michal Vasko26743a22022-03-29 14:48:10 +0200631 const struct lyd_node *prev_parent;
Michal Vasko23b51a82022-03-29 14:22:34 +0200632 struct lyd_node_opaq *opaq = NULL;
Radek Krejci857189e2020-09-01 13:26:36 +0200633 ly_bool has_content = 0;
Radek Krejci5536d282020-08-04 23:27:44 +0200634
Michal Vasko630d9892020-12-08 17:11:08 +0100635 LY_LIST_FOR(lyd_child(node), child) {
Michal Vasko8db584d2022-03-30 13:42:48 +0200636 if (lyd_node_should_print(child, pctx->options)) {
Michal Vasko630d9892020-12-08 17:11:08 +0100637 break;
638 }
639 }
640 if (node->meta || child) {
Radek Krejci5536d282020-08-04 23:27:44 +0200641 has_content = 1;
Radek Krejci5536d282020-08-04 23:27:44 +0200642 }
Michal Vasko23b51a82022-03-29 14:22:34 +0200643 if (!node->schema) {
644 opaq = (struct lyd_node_opaq *)node;
645 }
Radek Krejci5536d282020-08-04 23:27:44 +0200646
Michal Vasko1a85d332021-08-27 10:35:28 +0200647 if ((node->schema && (node->schema->nodetype == LYS_LIST)) ||
Michal Vasko23b51a82022-03-29 14:22:34 +0200648 (opaq && (opaq->hints != LYD_HINT_DATA) && (opaq->hints & LYD_NODEHINT_LIST))) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100649 ly_print_(pctx->out, "%s%*s{%s", (is_open_array(pctx, node) && (pctx->level_printed >= pctx->level)) ?
Michal Vasko1a85d332021-08-27 10:35:28 +0200650 (DO_FORMAT ? ",\n" : ",") : "", INDENT, (DO_FORMAT && has_content) ? "\n" : "");
651 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100652 ly_print_(pctx->out, "%s{%s", (is_open_array(pctx, node) && (pctx->level_printed >= pctx->level)) ? "," : "",
Radek Krejci0f969882020-08-21 16:56:47 +0200653 (DO_FORMAT && has_content) ? "\n" : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200654 }
655 LEVEL_INC;
656
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100657 json_print_attributes(pctx, node, 1);
Radek Krejci5536d282020-08-04 23:27:44 +0200658
Michal Vasko76096ec2022-02-24 16:06:16 +0100659 /* print children */
Michal Vasko26743a22022-03-29 14:48:10 +0200660 prev_parent = pctx->parent;
661 pctx->parent = node;
Michal Vasko76096ec2022-02-24 16:06:16 +0100662 LY_LIST_FOR(lyd_child(node), child) {
663 LY_CHECK_RET(json_print_node(pctx, child));
Radek Krejci5536d282020-08-04 23:27:44 +0200664 }
Michal Vasko26743a22022-03-29 14:48:10 +0200665 pctx->parent = prev_parent;
Radek Krejci5536d282020-08-04 23:27:44 +0200666
Radek Krejci5536d282020-08-04 23:27:44 +0200667 LEVEL_DEC;
668 if (DO_FORMAT && has_content) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100669 ly_print_(pctx->out, "\n%*s}", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200670 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100671 ly_print_(pctx->out, "}");
Radek Krejci5536d282020-08-04 23:27:44 +0200672 }
673 LEVEL_PRINTED;
674
675 return LY_SUCCESS;
676}
677
678/**
679 * @brief Print container data node including its metadata.
680 *
681 * @param[in] ctx JSON printer context.
682 * @param[in] node Data node to print.
683 * @return LY_ERR value.
684 */
685static int
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100686json_print_container(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200687{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100688 LY_CHECK_RET(json_print_member(pctx, node, 0));
689 LY_CHECK_RET(json_print_inner(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200690
691 return LY_SUCCESS;
692}
693
694/**
Michal Vasko76096ec2022-02-24 16:06:16 +0100695 * @brief Print anydata/anyxml data node including its metadata.
Michal Vaskobbdadda2022-01-06 11:40:10 +0100696 *
697 * @param[in] ctx JSON printer context.
698 * @param[in] node Data node to print.
699 * @return LY_ERR value.
700 */
701static int
Michal Vasko76096ec2022-02-24 16:06:16 +0100702json_print_any(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Michal Vaskobbdadda2022-01-06 11:40:10 +0100703{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100704 LY_CHECK_RET(json_print_member(pctx, node, 0));
705 LY_CHECK_RET(json_print_any_content(pctx, (struct lyd_node_any *)node));
Michal Vaskobbdadda2022-01-06 11:40:10 +0100706 LEVEL_PRINTED;
707
708 /* print attributes as sibling */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100709 json_print_attributes(pctx, node, 0);
Michal Vaskobbdadda2022-01-06 11:40:10 +0100710
711 return LY_SUCCESS;
712}
713
714/**
Michal Vaskoaa22f422021-12-02 10:48:32 +0100715 * @brief Check whether a node is the last printed instance of a (leaf-)list.
716 *
717 * @param[in] ctx JSON printer context.
718 * @param[in] node Last printed node.
719 * @return Whether it is the last printed instance or not.
720 */
721static ly_bool
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100722json_print_array_is_last_inst(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Michal Vaskoaa22f422021-12-02 10:48:32 +0100723{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100724 if (!is_open_array(pctx, node)) {
Michal Vasko06217f32021-12-09 09:25:50 +0100725 /* no array open */
726 return 0;
727 }
728
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100729 if ((pctx->root == node) && !(pctx->options & LYD_PRINT_WITHSIBLINGS)) {
Michal Vaskoaa22f422021-12-02 10:48:32 +0100730 /* the only printed instance */
731 return 1;
732 }
733
Michal Vasko06217f32021-12-09 09:25:50 +0100734 if (!node->next || (node->next->schema != node->schema)) {
Michal Vaskoaa22f422021-12-02 10:48:32 +0100735 /* last instance */
736 return 1;
737 }
738
739 return 0;
740}
741
742/**
Radek Krejci5536d282020-08-04 23:27:44 +0200743 * @brief Print single leaf-list or list instance.
744 *
745 * In case of list, metadata are printed inside the list object. For the leaf-list,
746 * metadata are marked in the context for later printing after closing the array next to it using
747 * json_print_metadata_leaflist().
748 *
749 * @param[in] ctx JSON printer context.
750 * @param[in] node Data node to print.
751 * @return LY_ERR value.
752 */
753static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100754json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200755{
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100756 if (!is_open_array(pctx, node)) {
757 LY_CHECK_RET(json_print_member(pctx, node, 0));
758 LY_CHECK_RET(json_print_array_open(pctx, node));
Radek Krejciee74a192021-04-09 09:55:34 +0200759 if (node->schema->nodetype == LYS_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100760 ly_print_(pctx->out, "%*s", INDENT);
Radek Krejciee74a192021-04-09 09:55:34 +0200761 }
Radek Krejci5536d282020-08-04 23:27:44 +0200762 } else if (node->schema->nodetype == LYS_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100763 ly_print_(pctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200764 }
765
766 if (node->schema->nodetype == LYS_LIST) {
Radek Krejcia1c1e542020-09-29 16:06:52 +0200767 if (!lyd_child(node)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200768 /* empty, e.g. in case of filter */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100769 ly_print_(pctx->out, "%s%snull", (pctx->level_printed >= pctx->level) ? "," : "", DO_FORMAT ? " " : "");
Radek Krejci5536d282020-08-04 23:27:44 +0200770 LEVEL_PRINTED;
771 } else {
772 /* print list's content */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100773 LY_CHECK_RET(json_print_inner(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200774 }
775 } else {
776 assert(node->schema->nodetype == LYS_LEAFLIST);
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100777 LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
Radek Krejci5536d282020-08-04 23:27:44 +0200778
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100779 if (node->meta && !pctx->print_sibling_metadata) {
780 pctx->print_sibling_metadata = node;
Radek Krejci5536d282020-08-04 23:27:44 +0200781 }
782 }
783
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100784 if (json_print_array_is_last_inst(pctx, node)) {
785 json_print_array_close(pctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200786 }
787
788 return LY_SUCCESS;
789}
790
791/**
792 * @brief Print leaf-list's metadata in case they were marked in the last call to json_print_leaf_list().
793 * This function is supposed to be called when the leaf-list array is closed.
794 *
795 * @param[in] ctx JSON printer context.
796 * @return LY_ERR value.
797 */
798static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100799json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
Radek Krejci5536d282020-08-04 23:27:44 +0200800{
801 const struct lyd_node *prev, *node, *iter;
802
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100803 if (!pctx->print_sibling_metadata) {
Radek Krejci5536d282020-08-04 23:27:44 +0200804 return LY_SUCCESS;
805 }
806
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100807 for (node = pctx->print_sibling_metadata, prev = pctx->print_sibling_metadata->prev;
Radek Krejci5536d282020-08-04 23:27:44 +0200808 prev->next && matching_node(node, prev);
809 node = prev, prev = node->prev) {}
810
811 /* node is the first instance of the leaf-list */
812
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100813 LY_CHECK_RET(json_print_member(pctx, node, 1));
814 ly_print_(pctx->out, "[%s", (DO_FORMAT ? "\n" : ""));
Radek Krejci5536d282020-08-04 23:27:44 +0200815 LEVEL_INC;
816 LY_LIST_FOR(node, iter) {
817 PRINT_COMMA;
818 if (iter->meta) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100819 ly_print_(pctx->out, "%*s%s", INDENT, DO_FORMAT ? "{\n" : "{");
Radek Krejci5536d282020-08-04 23:27:44 +0200820 LEVEL_INC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100821 LY_CHECK_RET(json_print_metadata(pctx, iter, NULL));
Radek Krejci5536d282020-08-04 23:27:44 +0200822 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100823 ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200824 } else {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100825 ly_print_(pctx->out, "null");
Radek Krejci5536d282020-08-04 23:27:44 +0200826 }
827 LEVEL_PRINTED;
828 if (!matching_node(iter, iter->next)) {
829 break;
830 }
831 }
832 LEVEL_DEC;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100833 ly_print_(pctx->out, "%s%*s]", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200834 LEVEL_PRINTED;
835
836 return LY_SUCCESS;
837}
838
839/**
840 * @brief Print opaq data node including its attributes.
841 *
842 * @param[in] ctx JSON printer context.
843 * @param[in] node Opaq node to print.
844 * @return LY_ERR value.
845 */
846static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100847json_print_opaq(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200848{
Radek Krejci857189e2020-09-01 13:26:36 +0200849 ly_bool first = 1, last = 1;
Radek Krejci5536d282020-08-04 23:27:44 +0200850
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200851 if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
Michal Vasko9e685082021-01-29 14:49:09 +0100852 if (node->prev->next && matching_node(node->prev, &node->node)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200853 first = 0;
854 }
Michal Vasko9e685082021-01-29 14:49:09 +0100855 if (node->next && matching_node(&node->node, node->next)) {
Radek Krejci5536d282020-08-04 23:27:44 +0200856 last = 0;
857 }
858 }
859
860 if (first) {
Michal Vasko26743a22022-03-29 14:48:10 +0200861 LY_CHECK_RET(json_print_member2(pctx, pctx->parent, node->format, &node->name, 0));
Radek Krejci5536d282020-08-04 23:27:44 +0200862
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200863 if (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100864 LY_CHECK_RET(json_print_array_open(pctx, &node->node));
Radek Krejci5536d282020-08-04 23:27:44 +0200865 }
Michal Vasko1a85d332021-08-27 10:35:28 +0200866 if (node->hints & LYD_NODEHINT_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100867 ly_print_(pctx->out, "%*s", INDENT);
Michal Vasko1a85d332021-08-27 10:35:28 +0200868 }
869 } else if (node->hints & LYD_NODEHINT_LEAFLIST) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100870 ly_print_(pctx->out, ",%s%*s", DO_FORMAT ? "\n" : "", INDENT);
Radek Krejci5536d282020-08-04 23:27:44 +0200871 }
Michal Vasko1a85d332021-08-27 10:35:28 +0200872 if (node->child || (node->hints & LYD_NODEHINT_LIST)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100873 LY_CHECK_RET(json_print_inner(pctx, &node->node));
Radek Krejci5536d282020-08-04 23:27:44 +0200874 LEVEL_PRINTED;
875 } else {
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200876 if (node->hints & LYD_VALHINT_EMPTY) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100877 ly_print_(pctx->out, "[null]");
Michal Vaskocea58712022-04-01 14:37:08 +0200878 } else if ((node->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) && !(node->hints & LYD_VALHINT_NUM64)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100879 ly_print_(pctx->out, "%s", node->value);
Radek Krejci5536d282020-08-04 23:27:44 +0200880 } else {
Michal Vaskocea58712022-04-01 14:37:08 +0200881 /* string or a large number */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100882 ly_print_(pctx->out, "\"%s\"", node->value);
Radek Krejci5536d282020-08-04 23:27:44 +0200883 }
884 LEVEL_PRINTED;
885
886 /* attributes */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100887 json_print_attributes(pctx, (const struct lyd_node *)node, 0);
Radek Krejci5536d282020-08-04 23:27:44 +0200888
889 }
Michal Vaskofeca4fb2020-10-05 08:58:40 +0200890 if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100891 json_print_array_close(pctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200892 LEVEL_PRINTED;
893 }
894
895 return LY_SUCCESS;
896}
897
898/**
899 * @brief Print all the types of data node including its metadata.
900 *
901 * @param[in] ctx JSON printer context.
902 * @param[in] node Data node to print.
903 * @return LY_ERR value.
904 */
905static LY_ERR
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100906json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
Radek Krejci5536d282020-08-04 23:27:44 +0200907{
Michal Vasko8db584d2022-03-30 13:42:48 +0200908 if (!lyd_node_should_print(node, pctx->options)) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100909 if (json_print_array_is_last_inst(pctx, node)) {
910 json_print_array_close(pctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200911 }
912 return LY_SUCCESS;
913 }
914
915 if (!node->schema) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100916 LY_CHECK_RET(json_print_opaq(pctx, (const struct lyd_node_opaq *)node));
Radek Krejci5536d282020-08-04 23:27:44 +0200917 } else {
918 switch (node->schema->nodetype) {
919 case LYS_RPC:
920 case LYS_ACTION:
921 case LYS_NOTIF:
922 case LYS_CONTAINER:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100923 LY_CHECK_RET(json_print_container(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200924 break;
925 case LYS_LEAF:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100926 LY_CHECK_RET(json_print_leaf(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200927 break;
928 case LYS_LEAFLIST:
929 case LYS_LIST:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100930 LY_CHECK_RET(json_print_leaf_list(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200931 break;
Michal Vasko76096ec2022-02-24 16:06:16 +0100932 case LYS_ANYDATA:
Radek Krejci5536d282020-08-04 23:27:44 +0200933 case LYS_ANYXML:
Michal Vasko76096ec2022-02-24 16:06:16 +0100934 LY_CHECK_RET(json_print_any(pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200935 break;
936 default:
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100937 LOGINT(pctx->ctx);
Radek Krejci5536d282020-08-04 23:27:44 +0200938 return EXIT_FAILURE;
939 }
940 }
941
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100942 pctx->level_printed = pctx->level;
Radek Krejci5536d282020-08-04 23:27:44 +0200943
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100944 if (pctx->print_sibling_metadata && !matching_node(node->next, pctx->print_sibling_metadata)) {
945 json_print_metadata_leaflist(pctx);
946 pctx->print_sibling_metadata = NULL;
Radek Krejci5536d282020-08-04 23:27:44 +0200947 }
948
949 return LY_SUCCESS;
950}
951
952LY_ERR
Radek Krejci1deb5be2020-08-26 16:43:36 +0200953json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options)
Radek Krejci5536d282020-08-04 23:27:44 +0200954{
955 const struct lyd_node *node;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100956 struct jsonpr_ctx pctx = {0};
Radek Krejci52f65552020-09-01 17:03:35 +0200957 const char *delimiter = (options & LYD_PRINT_SHRINK) ? "" : "\n";
Radek Krejci5536d282020-08-04 23:27:44 +0200958
Radek Krejci2e874772020-08-28 16:36:33 +0200959 if (!root) {
960 ly_print_(out, "{}%s", delimiter);
961 ly_print_flush(out);
962 return LY_SUCCESS;
963 }
964
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100965 pctx.out = out;
Michal Vasko26743a22022-03-29 14:48:10 +0200966 pctx.parent = NULL;
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100967 pctx.level = 1;
968 pctx.level_printed = 0;
969 pctx.options = options;
970 pctx.ctx = LYD_CTX(root);
Radek Krejci5536d282020-08-04 23:27:44 +0200971
972 /* start */
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100973 ly_print_(pctx.out, "{%s", delimiter);
Radek Krejci5536d282020-08-04 23:27:44 +0200974
975 /* content */
976 LY_LIST_FOR(root, node) {
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100977 pctx.root = node;
978 LY_CHECK_RET(json_print_node(&pctx, node));
Radek Krejci5536d282020-08-04 23:27:44 +0200979 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
980 break;
981 }
982 }
983
984 /* end */
Michal Vasko5233e962020-08-14 14:26:20 +0200985 ly_print_(out, "%s}%s", delimiter, delimiter);
Radek Krejci5536d282020-08-04 23:27:44 +0200986
Michal Vasko61ad1ff2022-02-10 15:48:39 +0100987 assert(!pctx.open.count);
988 ly_set_erase(&pctx.open, NULL);
Radek Krejci5536d282020-08-04 23:27:44 +0200989
990 ly_print_flush(out);
991 return LY_SUCCESS;
992}