Coverage Report

Created: 2025-08-03 06:36

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