Skip to content

Commit 34855e9

Browse files
committed
printer data UPDATE support printing empty (leaf-)lists
1 parent 8b5cb05 commit 34855e9

File tree

3 files changed

+187
-37
lines changed

3 files changed

+187
-37
lines changed

src/printer_data.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ struct ly_out;
9191
be shrinked to include only the necessary data making them smaller
9292
but requiring validation. */
9393
#define LYD_PRINT_KEEPEMPTYCONT 0x04 /**< Preserve empty non-presence containers */
94+
#define LYD_PRINT_EMPTY_LEAF_LIST 0x08 /**< Print even empty list and leaf-list instances, not possible for every
95+
data format (supported only for ::LYD_JSON). */
9496
#define LYD_PRINT_WD_MASK 0xF0 /**< Mask for with-defaults modes */
9597
#define LYD_PRINT_WD_EXPLICIT 0x00 /**< Explicit with-defaults mode. Only the data explicitly being present in
9698
the data tree are printed, so the implicitly added default nodes are

src/printer_json.c

Lines changed: 136 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,22 @@
3232
#include "set.h"
3333
#include "tree.h"
3434
#include "tree_data.h"
35+
#include "tree_data_internal.h"
3536
#include "tree_schema.h"
3637

3738
/**
3839
* @brief JSON printer context.
3940
*/
4041
struct jsonpr_ctx {
41-
struct ly_out *out; /**< output specification */
42+
struct ly_out *out; /**< output specification */
4243
const struct lyd_node *root; /**< root node of the subtree being printed */
4344
const struct lyd_node *parent; /**< parent of the node being printed */
44-
uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
45-
uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
46-
const struct ly_ctx *ctx; /**< libyang context */
45+
uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
46+
uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */
47+
const struct ly_ctx *ctx; /**< libyang context */
4748

48-
uint16_t level_printed; /* level where some data were already printed */
49-
struct ly_set open; /* currently open array(s) */
49+
uint16_t level_printed; /**< level where some data were already printed */
50+
struct ly_set open; /**< currently open array(s) */
5051
const struct lyd_node *first_leaflist; /**< first printed leaf-list instance, used when printing its metadata/attributes */
5152
};
5253

@@ -61,6 +62,10 @@ struct jsonpr_ctx {
6162
ly_print_(pctx->out, ",%s", (DO_FORMAT ? "\n" : "")); \
6263
}
6364

65+
static LY_ERR json_print_leaf_list_empty(struct jsonpr_ctx *pctx, const struct lysc_node *snode);
66+
static const struct lysc_node *json_print_next_empty_leaf_list(struct jsonpr_ctx *pctx, const struct lysc_node *snode,
67+
const struct lyd_node *siblings);
68+
6469
static LY_ERR json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node);
6570

6671
/**
@@ -144,27 +149,28 @@ json_print_array_close(struct jsonpr_ctx *pctx)
144149
* @brief Get the node's module name to use as the @p node prefix in JSON.
145150
*
146151
* @param[in] node Node to process.
152+
* @param[in] snode Schema node to process.
147153
* @param[out] mod_name Module name of @p node, can be NULL (format is XML and the module with NS is not in the context).
148154
* @param[out] data_dict Whether @p mod_name is from the schema or data dictionary.
149155
*/
150156
static void
151-
node_prefix(const struct lyd_node *node, const char **mod_name, ly_bool *data_dict)
157+
node_prefix(const struct lyd_node *node, const struct lysc_node *snode, const char **mod_name, ly_bool *data_dict)
152158
{
153159
struct lyd_node_opaq *onode;
154160
const struct lys_module *mod;
155161

156162
*mod_name = NULL;
157163

158-
if (!node) {
164+
if (!node && !snode) {
159165
return;
160166
}
161167

162-
if (node->schema) {
163-
*mod_name = node->schema->module->name;
168+
if (snode) {
169+
*mod_name = snode->module->name;
164170
if (data_dict) {
165171
*data_dict = 0;
166172
}
167-
} else {
173+
} else if (node) {
168174
onode = (struct lyd_node_opaq *)node;
169175

170176
switch (onode->format) {
@@ -201,27 +207,27 @@ node_prefix(const struct lyd_node *node, const char **mod_name, ly_bool *data_di
201207
* @return 0 in case the nodes' modules are the same
202208
* @return 1 in case the nodes belongs to different modules
203209
*/
204-
int
205-
json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
210+
static int
211+
json_nscmp(const struct lyd_node *node1, const struct lysc_node *snode1, const struct lyd_node *node2)
206212
{
207213
const char *pref1, *pref2;
208214
ly_bool dd1, dd2;
209215

210-
assert(node1 || node2);
216+
assert(snode1 || node2);
211217

212-
if (!node1 || !node2) {
218+
if (!snode1 || !node2) {
213219
return 1;
214-
} else if (node1->schema && node2->schema) {
215-
if (node1->schema->module == node2->schema->module) {
220+
} else if (snode1 && node2->schema) {
221+
if (snode1->module == node2->schema->module) {
216222
/* belongs to the same module */
217223
return 0;
218224
} else {
219225
/* different modules */
220226
return 1;
221227
}
222228
} else {
223-
node_prefix(node1, &pref1, &dd1);
224-
node_prefix(node2, &pref2, &dd2);
229+
node_prefix(node1, snode1, &pref1, &dd1);
230+
node_prefix(node2, node2->schema, &pref2, &dd2);
225231

226232
if (pref1 && pref2 && (((dd1 == dd2) && (pref1 == pref2)) || ((dd1 != dd2) && !strcmp(pref1, pref2)))) {
227233
return 0;
@@ -284,23 +290,32 @@ json_print_string(struct ly_out *out, const char *text)
284290
* @brief Print JSON object's member name, ending by ':'. It resolves if the prefix is supposed to be printed.
285291
*
286292
* @param[in] ctx JSON printer context.
287-
* @param[in] node The data node being printed.
293+
* @param[in] node Data node being printed.
294+
* @param[in] snode Optional schema node if there is no @p node.
288295
* @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier.
289296
* @return LY_ERR value.
290297
*/
291298
static LY_ERR
292-
json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr)
299+
json_print_member(struct jsonpr_ctx *pctx, const struct lyd_node *node, const struct lysc_node *snode, ly_bool is_attr)
293300
{
294-
const char *pref;
301+
const char *pref, *name;
302+
303+
assert(node || snode);
304+
305+
if (node) {
306+
snode = node->schema;
307+
name = LYD_NAME(node);
308+
} else {
309+
name = snode->name;
310+
}
295311

296312
PRINT_COMMA;
297-
if ((LEVEL == 1) || json_nscmp(node, pctx->parent)) {
313+
if ((LEVEL == 1) || json_nscmp(node, snode, pctx->parent)) {
298314
/* print "namespace" */
299-
node_prefix(node, &pref, NULL);
300-
ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "",
301-
pref, node->schema->name, DO_FORMAT ? " " : "");
315+
node_prefix(node, snode, &pref, NULL);
316+
ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "", pref, name, DO_FORMAT ? " " : "");
302317
} else {
303-
ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", node->schema->name, DO_FORMAT ? " " : "");
318+
ly_print_(pctx->out, "%*s\"%s%s\":%s", INDENT, is_attr ? "@" : "", name, DO_FORMAT ? " " : "");
304319
}
305320

306321
return LY_SUCCESS;
@@ -351,7 +366,7 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA
351366
}
352367

353368
/* print the member, strcmp because node prefix is in the schema dict, module_name in data dict */
354-
node_prefix(parent, &pmod_name, NULL);
369+
node_prefix(parent, parent ? parent->schema : NULL, &pmod_name, NULL);
355370
if (module_name && (!parent || strcmp(pmod_name, module_name))) {
356371
ly_print_(pctx->out, "%*s\"%s%s:%s\":%s", INDENT, is_attr ? "@" : "", module_name, name_str, DO_FORMAT ? " " : "");
357372
} else {
@@ -541,7 +556,7 @@ json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_b
541556
if (inner) {
542557
LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1));
543558
} else {
544-
LY_CHECK_RET(json_print_member(pctx, node, 1));
559+
LY_CHECK_RET(json_print_member(pctx, node, NULL, 1));
545560
}
546561
ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
547562
LEVEL_INC;
@@ -577,7 +592,7 @@ json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_b
577592
static LY_ERR
578593
json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
579594
{
580-
LY_CHECK_RET(json_print_member(pctx, node, 0));
595+
LY_CHECK_RET(json_print_member(pctx, node, NULL, 0));
581596
LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
582597
LEVEL_PRINTED;
583598

@@ -680,14 +695,17 @@ json_print_inner(struct jsonpr_ctx *pctx, const struct lyd_node *node)
680695
struct lyd_node *child;
681696
const struct lyd_node *prev_parent;
682697
struct lyd_node_opaq *opaq = NULL;
683-
ly_bool has_content = 0;
698+
const struct lysc_node *snode;
699+
ly_bool has_content = 0, no_child_print = 1;
684700

685701
LY_LIST_FOR(lyd_child(node), child) {
686702
if (lyd_node_should_print(child, pctx->options)) {
703+
no_child_print = 0;
687704
break;
688705
}
689706
}
690-
if (node->meta || child) {
707+
if (node->meta || child || ((pctx->options & LYD_PRINT_EMPTY_LEAF_LIST) &&
708+
json_print_next_empty_leaf_list(pctx, lysc_node_child(node->schema), lyd_child(node)))) {
691709
has_content = 1;
692710
}
693711
if (!node->schema) {
@@ -714,6 +732,16 @@ json_print_inner(struct jsonpr_ctx *pctx, const struct lyd_node *node)
714732
}
715733
pctx->parent = prev_parent;
716734

735+
if (no_child_print && (pctx->options & LYD_PRINT_EMPTY_LEAF_LIST)) {
736+
snode = json_print_next_empty_leaf_list(pctx, lysc_node_child(node->schema), NULL);
737+
while (snode) {
738+
/* print an empty (leaf-)list */
739+
LY_CHECK_RET(json_print_leaf_list_empty(pctx, snode));
740+
741+
snode = json_print_next_empty_leaf_list(pctx, snode->next, NULL);
742+
}
743+
}
744+
717745
LEVEL_DEC;
718746
if (DO_FORMAT && has_content) {
719747
ly_print_(pctx->out, "\n%*s}", INDENT);
@@ -735,7 +763,7 @@ json_print_inner(struct jsonpr_ctx *pctx, const struct lyd_node *node)
735763
static int
736764
json_print_container(struct jsonpr_ctx *pctx, const struct lyd_node *node)
737765
{
738-
LY_CHECK_RET(json_print_member(pctx, node, 0));
766+
LY_CHECK_RET(json_print_member(pctx, node, NULL, 0));
739767
LY_CHECK_RET(json_print_inner(pctx, node));
740768

741769
return LY_SUCCESS;
@@ -751,7 +779,7 @@ json_print_container(struct jsonpr_ctx *pctx, const struct lyd_node *node)
751779
static int
752780
json_print_any(struct jsonpr_ctx *pctx, const struct lyd_node *node)
753781
{
754-
LY_CHECK_RET(json_print_member(pctx, node, 0));
782+
LY_CHECK_RET(json_print_member(pctx, node, NULL, 0));
755783
LY_CHECK_RET(json_print_any_content(pctx, (struct lyd_node_any *)node));
756784
LEVEL_PRINTED;
757785

@@ -806,7 +834,7 @@ json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
806834
const struct lys_module *df_mod = NULL;
807835

808836
if (!is_open_array(pctx, node)) {
809-
LY_CHECK_RET(json_print_member(pctx, node, 0));
837+
LY_CHECK_RET(json_print_member(pctx, node, NULL, 0));
810838
LY_CHECK_RET(json_print_array_open(pctx, node));
811839
if (node->schema->nodetype == LYS_LEAFLIST) {
812840
ly_print_(pctx->out, "%*s", INDENT);
@@ -845,6 +873,34 @@ json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
845873
return LY_SUCCESS;
846874
}
847875

876+
/**
877+
* @brief Print empty leaf-list or list instance.
878+
*
879+
* @param[in] ctx JSON printer context.
880+
* @param[in] snode Schema node to print.
881+
* @return LY_ERR value.
882+
*/
883+
static LY_ERR
884+
json_print_leaf_list_empty(struct jsonpr_ctx *pctx, const struct lysc_node *snode)
885+
{
886+
assert(snode->nodetype & (LYS_LEAFLIST | LYS_LIST));
887+
888+
LY_CHECK_RET(json_print_member(pctx, NULL, snode, 0));
889+
890+
/* avoid an empty line */
891+
if (!(pctx->options & LY_PRINT_SHRINK)) {
892+
pctx->options |= LY_PRINT_SHRINK;
893+
LY_CHECK_RET(json_print_array_open(pctx, (void *)snode));
894+
pctx->options &= ~LY_PRINT_SHRINK;
895+
} else {
896+
LY_CHECK_RET(json_print_array_open(pctx, (void *)snode));
897+
}
898+
899+
json_print_array_close(pctx);
900+
901+
return LY_SUCCESS;
902+
}
903+
848904
/**
849905
* @brief Print leaf-list's metadata or opaque nodes attributes.
850906
* This function is supposed to be called when the leaf-list array is closed.
@@ -874,7 +930,7 @@ json_print_meta_attr_leaflist(struct jsonpr_ctx *pctx)
874930
node = prev, prev = node->prev) {}
875931

876932
if (node->schema) {
877-
LY_CHECK_RET(json_print_member(pctx, node, 1));
933+
LY_CHECK_RET(json_print_member(pctx, node, NULL, 1));
878934
} else {
879935
opaq = (struct lyd_node_opaq *)node;
880936
LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), opaq->format, &opaq->name, 1));
@@ -988,16 +1044,49 @@ json_print_opaq(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
9881044
return LY_SUCCESS;
9891045
}
9901046

1047+
/**
1048+
* @brief Return the following empty (leaf-)lists if no predecessor is printed.
1049+
*
1050+
* @param[in] pctx JSON printer context.
1051+
* @param[in] snode First schema node to consider.
1052+
* @param[in] siblings Siblings to check for (leaf-)list existence.
1053+
* @return Next (leaf-)list without instances.
1054+
* @return NULL if no (leaf-)list without instances matches snode or following nodes until a node is printed.
1055+
*/
1056+
static const struct lysc_node *
1057+
json_print_next_empty_leaf_list(struct jsonpr_ctx *pctx, const struct lysc_node *snode, const struct lyd_node *siblings)
1058+
{
1059+
struct lyd_node *match;
1060+
1061+
/* look for following empty (leaf-)lists */
1062+
LY_LIST_FOR(snode, snode) {
1063+
if (!lyd_find_sibling_schema(siblings, snode, &match) && lyd_node_should_print(match, pctx->options)) {
1064+
/* instance exists and will be printed */
1065+
break;
1066+
}
1067+
1068+
if (snode->nodetype & (LYS_LEAFLIST | LYS_LIST)) {
1069+
assert(!match);
1070+
return snode;
1071+
}
1072+
}
1073+
1074+
/* no empty (leaf-)list to print */
1075+
return NULL;
1076+
}
1077+
9911078
/**
9921079
* @brief Print all the types of data node including its metadata.
9931080
*
994-
* @param[in] ctx JSON printer context.
1081+
* @param[in] pctx JSON printer context.
9951082
* @param[in] node Data node to print.
9961083
* @return LY_ERR value.
9971084
*/
9981085
static LY_ERR
9991086
json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
10001087
{
1088+
const struct lysc_node *snode;
1089+
10011090
if (!lyd_node_should_print(node, pctx->options)) {
10021091
if (json_print_array_is_last_inst(pctx, node)) {
10031092
json_print_array_close(pctx);
@@ -1030,6 +1119,16 @@ json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
10301119
LOGINT(pctx->ctx);
10311120
return EXIT_FAILURE;
10321121
}
1122+
1123+
if (pctx->options & LYD_PRINT_EMPTY_LEAF_LIST) {
1124+
snode = json_print_next_empty_leaf_list(pctx, node->schema->next, node);
1125+
while (snode) {
1126+
/* print an empty (leaf-)list */
1127+
LY_CHECK_RET(json_print_leaf_list_empty(pctx, snode));
1128+
1129+
snode = json_print_next_empty_leaf_list(pctx, snode->next, node);
1130+
}
1131+
}
10331132
}
10341133

10351134
pctx->level_printed = pctx->level;

0 commit comments

Comments
 (0)