/src/libyang/src/printer_xml.c
Line | Count | Source |
1 | | /** |
2 | | * @file printer_xml.c |
3 | | * @author Michal Vasko <mvasko@cesnet.cz> |
4 | | * @author Radek Krejci <rkrejci@cesnet.cz> |
5 | | * @brief XML printer for libyang data structure |
6 | | * |
7 | | * Copyright (c) 2015 - 2019 CESNET, z.s.p.o. |
8 | | * |
9 | | * This source code is licensed under BSD 3-Clause License (the "License"). |
10 | | * You may not use this file except in compliance with the License. |
11 | | * You may obtain a copy of the License at |
12 | | * |
13 | | * https://opensource.org/licenses/BSD-3-Clause |
14 | | */ |
15 | | |
16 | | #include <assert.h> |
17 | | #include <stdint.h> |
18 | | #include <stdlib.h> |
19 | | #include <string.h> |
20 | | |
21 | | #include "common.h" |
22 | | #include "context.h" |
23 | | #include "dict.h" |
24 | | #include "log.h" |
25 | | #include "out.h" |
26 | | #include "out_internal.h" |
27 | | #include "parser_data.h" |
28 | | #include "plugins_types.h" |
29 | | #include "printer_data.h" |
30 | | #include "printer_internal.h" |
31 | | #include "set.h" |
32 | | #include "tree.h" |
33 | | #include "tree_data.h" |
34 | | #include "tree_schema.h" |
35 | | #include "xml.h" |
36 | | |
37 | | /** |
38 | | * @brief XML printer context. |
39 | | */ |
40 | | struct xmlpr_ctx { |
41 | | struct ly_out *out; /**< output specification */ |
42 | | uint16_t level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */ |
43 | | uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */ |
44 | | const struct ly_ctx *ctx; /**< libyang context */ |
45 | | struct ly_set prefix; /**< printed namespace prefixes */ |
46 | | struct ly_set ns; /**< printed namespaces */ |
47 | | }; |
48 | | |
49 | 0 | #define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */ |
50 | 0 | #define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */ |
51 | | |
52 | | /** |
53 | | * @brief Print a namespace if not already printed. |
54 | | * |
55 | | * @param[in] ctx XML printer context. |
56 | | * @param[in] ns Namespace to print, expected to be in dictionary. |
57 | | * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary. |
58 | | * @param[in] prefix_opts Prefix options changing the meaning of parameters. |
59 | | * @return Printed prefix of the namespace to use. |
60 | | */ |
61 | | static const char * |
62 | | xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, uint32_t prefix_opts) |
63 | 0 | { |
64 | 0 | uint32_t i; |
65 | |
|
66 | 0 | for (i = ctx->ns.count; i > 0; --i) { |
67 | 0 | if (!new_prefix) { |
68 | | /* find default namespace */ |
69 | 0 | if (!ctx->prefix.objs[i - 1]) { |
70 | 0 | if (!strcmp(ctx->ns.objs[i - 1], ns)) { |
71 | | /* matching default namespace */ |
72 | 0 | return ctx->prefix.objs[i - 1]; |
73 | 0 | } |
74 | | /* not matching default namespace */ |
75 | 0 | break; |
76 | 0 | } |
77 | 0 | } else { |
78 | | /* find prefixed namespace */ |
79 | 0 | if (!strcmp(ctx->ns.objs[i - 1], ns)) { |
80 | 0 | if (!ctx->prefix.objs[i - 1]) { |
81 | | /* default namespace is not interesting */ |
82 | 0 | continue; |
83 | 0 | } |
84 | | |
85 | 0 | if (!strcmp(ctx->prefix.objs[i - 1], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) { |
86 | | /* the same prefix or can be any */ |
87 | 0 | return ctx->prefix.objs[i - 1]; |
88 | 0 | } |
89 | 0 | } |
90 | 0 | } |
91 | 0 | } |
92 | | |
93 | | /* suitable namespace not found, must be printed */ |
94 | 0 | ly_print_(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns); |
95 | | |
96 | | /* and added into namespaces */ |
97 | 0 | if (new_prefix) { |
98 | 0 | LY_CHECK_RET(lydict_insert(ctx->ctx, new_prefix, 0, &new_prefix), NULL); |
99 | 0 | } |
100 | 0 | LY_CHECK_RET(ly_set_add(&ctx->prefix, (void *)new_prefix, 1, NULL), NULL); |
101 | 0 | LY_CHECK_RET(ly_set_add(&ctx->ns, (void *)ns, 1, &i), NULL); |
102 | | |
103 | | /* return it */ |
104 | 0 | return ctx->prefix.objs[i]; |
105 | 0 | } |
106 | | |
107 | | static const char * |
108 | | xml_print_ns_opaq(struct xmlpr_ctx *ctx, LY_VALUE_FORMAT format, const struct ly_opaq_name *name, uint32_t prefix_opts) |
109 | 0 | { |
110 | 0 | switch (format) { |
111 | 0 | case LY_VALUE_XML: |
112 | 0 | return xml_print_ns(ctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts); |
113 | 0 | break; |
114 | 0 | case LY_VALUE_JSON: |
115 | 0 | if (name->module_name) { |
116 | 0 | const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, name->module_name); |
117 | 0 | if (mod) { |
118 | 0 | return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts); |
119 | 0 | } |
120 | 0 | } |
121 | 0 | break; |
122 | 0 | default: |
123 | | /* cannot be created */ |
124 | 0 | LOGINT(ctx->ctx); |
125 | 0 | } |
126 | | |
127 | 0 | return NULL; |
128 | 0 | } |
129 | | |
130 | | /** |
131 | | * @brief Print prefix data. |
132 | | * |
133 | | * @param[in] ctx XML printer context. |
134 | | * @param[in] format Value prefix format, only ::LY_VALUE_XML supported. |
135 | | * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix). |
136 | | * @param[in] prefix_opts Prefix options changing the meaning of parameters. |
137 | | * @return LY_ERR value. |
138 | | */ |
139 | | static void |
140 | | xml_print_ns_prefix_data(struct xmlpr_ctx *ctx, LY_VALUE_FORMAT format, void *prefix_data, uint32_t prefix_opts) |
141 | 0 | { |
142 | 0 | const struct ly_set *set; |
143 | 0 | const struct lyxml_ns *ns; |
144 | 0 | uint32_t i; |
145 | |
|
146 | 0 | switch (format) { |
147 | 0 | case LY_VALUE_XML: |
148 | 0 | set = prefix_data; |
149 | 0 | for (i = 0; i < set->count; ++i) { |
150 | 0 | ns = set->objs[i]; |
151 | 0 | xml_print_ns(ctx, ns->uri, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : ns->prefix, prefix_opts); |
152 | 0 | } |
153 | 0 | break; |
154 | 0 | default: |
155 | | /* cannot be created */ |
156 | 0 | LOGINT(ctx->ctx); |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | /** |
161 | | * TODO |
162 | | */ |
163 | | static void |
164 | | xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node) |
165 | 0 | { |
166 | 0 | struct lyd_meta *meta; |
167 | 0 | const struct lys_module *mod; |
168 | 0 | struct ly_set ns_list = {0}; |
169 | |
|
170 | | #if 0 |
171 | | const char **prefs, **nss; |
172 | | const char *xml_expr = NULL, *mod_name; |
173 | | uint32_t ns_count, i; |
174 | | ly_bool rpc_filter = 0; |
175 | | char *p; |
176 | | size_t len; |
177 | | #endif |
178 | 0 | ly_bool dynamic; |
179 | | |
180 | | /* with-defaults */ |
181 | 0 | if (node->schema->nodetype & LYD_NODE_TERM) { |
182 | 0 | if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) || |
183 | 0 | ((ctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) { |
184 | | /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */ |
185 | 0 | mod = ly_ctx_get_module_latest(LYD_CTX(node), "ietf-netconf-with-defaults"); |
186 | 0 | if (mod) { |
187 | 0 | ly_print_(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0)); |
188 | 0 | } |
189 | 0 | } |
190 | 0 | } |
191 | | #if 0 |
192 | | /* technically, check for the extension get-filter-element-attributes from ietf-netconf */ |
193 | | if (!strcmp(node->schema->name, "filter") && |
194 | | (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) { |
195 | | rpc_filter = 1; |
196 | | } |
197 | | #endif |
198 | 0 | for (meta = node->meta; meta; meta = meta->next) { |
199 | 0 | const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list, |
200 | 0 | &dynamic, NULL); |
201 | | |
202 | | /* print namespaces connected with the value's prefixes */ |
203 | 0 | for (uint32_t u = 0; u < ns_list.count; ++u) { |
204 | 0 | mod = (const struct lys_module *)ns_list.objs[u]; |
205 | 0 | xml_print_ns(ctx, mod->ns, mod->prefix, 1); |
206 | 0 | } |
207 | 0 | ly_set_erase(&ns_list, NULL); |
208 | |
|
209 | | #if 0 |
210 | | if (rpc_filter) { |
211 | | /* exception for NETCONF's filter's attributes */ |
212 | | if (!strcmp(meta->name, "select")) { |
213 | | /* xpath content, we have to convert the JSON format into XML first */ |
214 | | xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count); |
215 | | if (!xml_expr) { |
216 | | /* error */ |
217 | | return EXIT_FAILURE; |
218 | | } |
219 | | |
220 | | for (i = 0; i < ns_count; ++i) { |
221 | | ly_print_(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]); |
222 | | } |
223 | | free(prefs); |
224 | | free(nss); |
225 | | } |
226 | | ly_print_(out, " %s=\"", meta->name); |
227 | | } else { |
228 | | #endif |
229 | | /* print the metadata with its namespace */ |
230 | 0 | mod = meta->annotation->module; |
231 | 0 | ly_print_(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name); |
232 | | #if 0 |
233 | | } |
234 | | #endif |
235 | | |
236 | | /* print metadata value */ |
237 | 0 | if (value && value[0]) { |
238 | 0 | lyxml_dump_text(ctx->out, value, 1); |
239 | 0 | } |
240 | 0 | ly_print_(ctx->out, "\""); |
241 | 0 | if (dynamic) { |
242 | 0 | free((void *)value); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | /** |
248 | | * @brief Print generic XML element despite of the data node type. |
249 | | * |
250 | | * Prints the element name, attributes and necessary namespaces. |
251 | | * |
252 | | * @param[in] ctx XML printer context. |
253 | | * @param[in] node Data node to be printed. |
254 | | */ |
255 | | static void |
256 | | xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node) |
257 | 0 | { |
258 | | /* print node name */ |
259 | 0 | ly_print_(ctx->out, "%*s<%s", INDENT, node->schema->name); |
260 | | |
261 | | /* print default namespace */ |
262 | 0 | xml_print_ns(ctx, node->schema->module->ns, NULL, 0); |
263 | | |
264 | | /* print metadata */ |
265 | 0 | xml_print_meta(ctx, node); |
266 | 0 | } |
267 | | |
268 | | static LY_ERR |
269 | | xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node) |
270 | 0 | { |
271 | 0 | const struct lyd_attr *attr; |
272 | 0 | const char *pref; |
273 | |
|
274 | 0 | LY_LIST_FOR(node->attr, attr) { |
275 | 0 | pref = NULL; |
276 | 0 | if (attr->name.prefix) { |
277 | | /* print attribute namespace */ |
278 | 0 | pref = xml_print_ns_opaq(ctx, attr->format, &attr->name, 0); |
279 | 0 | } |
280 | | |
281 | | /* print namespaces connected with the value's prefixes */ |
282 | 0 | if (attr->val_prefix_data) { |
283 | 0 | xml_print_ns_prefix_data(ctx, attr->format, attr->val_prefix_data, LYXML_PREFIX_REQUIRED); |
284 | 0 | } |
285 | | |
286 | | /* print the attribute with its prefix and value */ |
287 | 0 | ly_print_(ctx->out, " %s%s%s=\"", pref ? pref : "", pref ? ":" : "", attr->name.name); |
288 | 0 | lyxml_dump_text(ctx->out, attr->value, 1); |
289 | 0 | ly_print_(ctx->out, "\""); /* print attribute value terminator */ |
290 | |
|
291 | 0 | } |
292 | |
|
293 | 0 | return LY_SUCCESS; |
294 | 0 | } |
295 | | |
296 | | static LY_ERR |
297 | | xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node) |
298 | 0 | { |
299 | | /* print node name */ |
300 | 0 | ly_print_(ctx->out, "%*s<%s", INDENT, node->name.name); |
301 | | |
302 | | /* print default namespace */ |
303 | 0 | xml_print_ns_opaq(ctx, node->format, &node->name, LYXML_PREFIX_DEFAULT); |
304 | | |
305 | | /* print attributes */ |
306 | 0 | LY_CHECK_RET(xml_print_attr(ctx, node)); |
307 | |
|
308 | 0 | return LY_SUCCESS; |
309 | 0 | } |
310 | | |
311 | | static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node); |
312 | | |
313 | | /** |
314 | | * @brief Print XML element representing lyd_node_term. |
315 | | * |
316 | | * @param[in] ctx XML printer context. |
317 | | * @param[in] node Data node to be printed. |
318 | | */ |
319 | | static void |
320 | | xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node) |
321 | 0 | { |
322 | 0 | struct ly_set ns_list = {0}; |
323 | 0 | ly_bool dynamic; |
324 | 0 | const char *value; |
325 | |
|
326 | 0 | xml_print_node_open(ctx, &node->node); |
327 | 0 | value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML, |
328 | 0 | &ns_list, &dynamic, NULL); |
329 | | |
330 | | /* print namespaces connected with the values's prefixes */ |
331 | 0 | for (uint32_t u = 0; u < ns_list.count; ++u) { |
332 | 0 | const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u]; |
333 | 0 | ly_print_(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns); |
334 | 0 | } |
335 | 0 | ly_set_erase(&ns_list, NULL); |
336 | |
|
337 | 0 | if (!value || !value[0]) { |
338 | 0 | ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : ""); |
339 | 0 | } else { |
340 | 0 | ly_print_(ctx->out, ">"); |
341 | 0 | lyxml_dump_text(ctx->out, value, 0); |
342 | 0 | ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : ""); |
343 | 0 | } |
344 | 0 | if (dynamic) { |
345 | 0 | free((void *)value); |
346 | 0 | } |
347 | 0 | } |
348 | | |
349 | | /** |
350 | | * @brief Print XML element representing lyd_node_inner. |
351 | | * |
352 | | * @param[in] ctx XML printer context. |
353 | | * @param[in] node Data node to be printed. |
354 | | * @return LY_ERR value. |
355 | | */ |
356 | | static LY_ERR |
357 | | xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node) |
358 | 0 | { |
359 | 0 | LY_ERR ret; |
360 | 0 | struct lyd_node *child; |
361 | |
|
362 | 0 | xml_print_node_open(ctx, &node->node); |
363 | |
|
364 | 0 | LY_LIST_FOR(node->child, child) { |
365 | 0 | if (ly_should_print(child, ctx->options)) { |
366 | 0 | break; |
367 | 0 | } |
368 | 0 | } |
369 | 0 | if (!child) { |
370 | | /* there are no children that will be printed */ |
371 | 0 | ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : ""); |
372 | 0 | return LY_SUCCESS; |
373 | 0 | } |
374 | | |
375 | | /* children */ |
376 | 0 | ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : ""); |
377 | |
|
378 | 0 | LEVEL_INC; |
379 | 0 | LY_LIST_FOR(node->child, child) { |
380 | 0 | ret = xml_print_node(ctx, child); |
381 | 0 | LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret); |
382 | 0 | } |
383 | 0 | LEVEL_DEC; |
384 | |
|
385 | 0 | ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : ""); |
386 | |
|
387 | 0 | return LY_SUCCESS; |
388 | 0 | } |
389 | | |
390 | | static LY_ERR |
391 | | xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node) |
392 | 0 | { |
393 | 0 | struct lyd_node_any *any = (struct lyd_node_any *)node; |
394 | 0 | struct lyd_node *iter; |
395 | 0 | uint32_t prev_opts, prev_lo; |
396 | 0 | LY_ERR ret; |
397 | |
|
398 | 0 | xml_print_node_open(ctx, &node->node); |
399 | |
|
400 | 0 | if (!any->value.tree) { |
401 | | /* no content */ |
402 | 0 | no_content: |
403 | 0 | ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : ""); |
404 | 0 | return LY_SUCCESS; |
405 | 0 | } else { |
406 | 0 | if (any->value_type == LYD_ANYDATA_LYB) { |
407 | | /* turn logging off */ |
408 | 0 | prev_lo = ly_log_options(0); |
409 | | |
410 | | /* try to parse it into a data tree */ |
411 | 0 | if (lyd_parse_data_mem((struct ly_ctx *)LYD_CTX(node), any->value.mem, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &iter) == LY_SUCCESS) { |
412 | | /* successfully parsed */ |
413 | 0 | free(any->value.mem); |
414 | 0 | any->value.tree = iter; |
415 | 0 | any->value_type = LYD_ANYDATA_DATATREE; |
416 | 0 | } |
417 | | |
418 | | /* turn loggin on again */ |
419 | 0 | ly_log_options(prev_lo); |
420 | 0 | } |
421 | |
|
422 | 0 | switch (any->value_type) { |
423 | 0 | case LYD_ANYDATA_DATATREE: |
424 | | /* close opening tag and print data */ |
425 | 0 | prev_opts = ctx->options; |
426 | 0 | ctx->options &= ~LYD_PRINT_WITHSIBLINGS; |
427 | 0 | LEVEL_INC; |
428 | |
|
429 | 0 | ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : ""); |
430 | 0 | LY_LIST_FOR(any->value.tree, iter) { |
431 | 0 | ret = xml_print_node(ctx, iter); |
432 | 0 | LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret); |
433 | 0 | } |
434 | | |
435 | 0 | LEVEL_DEC; |
436 | 0 | ctx->options = prev_opts; |
437 | 0 | break; |
438 | 0 | case LYD_ANYDATA_STRING: |
439 | | /* escape XML-sensitive characters */ |
440 | 0 | if (!any->value.str[0]) { |
441 | 0 | goto no_content; |
442 | 0 | } |
443 | | /* close opening tag and print data */ |
444 | 0 | ly_print_(ctx->out, ">"); |
445 | 0 | lyxml_dump_text(ctx->out, any->value.str, 0); |
446 | 0 | break; |
447 | 0 | case LYD_ANYDATA_XML: |
448 | | /* print without escaping special characters */ |
449 | 0 | if (!any->value.str[0]) { |
450 | 0 | goto no_content; |
451 | 0 | } |
452 | 0 | ly_print_(ctx->out, ">%s", any->value.str); |
453 | 0 | break; |
454 | 0 | case LYD_ANYDATA_JSON: |
455 | 0 | case LYD_ANYDATA_LYB: |
456 | | /* JSON and LYB format is not supported */ |
457 | 0 | LOGWRN(LYD_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type); |
458 | 0 | goto no_content; |
459 | 0 | } |
460 | | |
461 | | /* closing tag */ |
462 | 0 | if (any->value_type == LYD_ANYDATA_DATATREE) { |
463 | 0 | ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, DO_FORMAT ? "\n" : ""); |
464 | 0 | } else { |
465 | 0 | ly_print_(ctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : ""); |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | 0 | return LY_SUCCESS; |
470 | 0 | } |
471 | | |
472 | | static LY_ERR |
473 | | xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node) |
474 | 0 | { |
475 | 0 | LY_ERR ret; |
476 | 0 | struct lyd_node *child; |
477 | |
|
478 | 0 | LY_CHECK_RET(xml_print_opaq_open(ctx, node)); |
479 | |
|
480 | 0 | if (node->value[0]) { |
481 | | /* print namespaces connected with the value's prefixes */ |
482 | 0 | if (node->val_prefix_data) { |
483 | 0 | xml_print_ns_prefix_data(ctx, node->format, node->val_prefix_data, LYXML_PREFIX_REQUIRED); |
484 | 0 | } |
485 | |
|
486 | 0 | ly_print_(ctx->out, ">"); |
487 | 0 | lyxml_dump_text(ctx->out, node->value, 0); |
488 | 0 | } |
489 | |
|
490 | 0 | if (node->child) { |
491 | | /* children */ |
492 | 0 | if (!node->value[0]) { |
493 | 0 | ly_print_(ctx->out, ">%s", DO_FORMAT ? "\n" : ""); |
494 | 0 | } |
495 | |
|
496 | 0 | LEVEL_INC; |
497 | 0 | LY_LIST_FOR(node->child, child) { |
498 | 0 | ret = xml_print_node(ctx, child); |
499 | 0 | LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret); |
500 | 0 | } |
501 | 0 | LEVEL_DEC; |
502 | |
|
503 | 0 | ly_print_(ctx->out, "%*s</%s>%s", INDENT, node->name.name, DO_FORMAT ? "\n" : ""); |
504 | 0 | } else if (node->value[0]) { |
505 | 0 | ly_print_(ctx->out, "</%s>%s", node->name.name, DO_FORMAT ? "\n" : ""); |
506 | 0 | } else { |
507 | | /* no value or children */ |
508 | 0 | ly_print_(ctx->out, "/>%s", DO_FORMAT ? "\n" : ""); |
509 | 0 | } |
510 | | |
511 | 0 | return LY_SUCCESS; |
512 | 0 | } |
513 | | |
514 | | /** |
515 | | * @brief Print XML element representing lyd_node. |
516 | | * |
517 | | * @param[in] ctx XML printer context. |
518 | | * @param[in] node Data node to be printed. |
519 | | * @return LY_ERR value. |
520 | | */ |
521 | | static LY_ERR |
522 | | xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node) |
523 | 0 | { |
524 | 0 | LY_ERR ret = LY_SUCCESS; |
525 | 0 | uint32_t ns_count; |
526 | |
|
527 | 0 | if (!ly_should_print(node, ctx->options)) { |
528 | | /* do not print at all */ |
529 | 0 | return LY_SUCCESS; |
530 | 0 | } |
531 | | |
532 | | /* remember namespace definition count on this level */ |
533 | 0 | ns_count = ctx->ns.count; |
534 | |
|
535 | 0 | if (!node->schema) { |
536 | 0 | ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node); |
537 | 0 | } else { |
538 | 0 | switch (node->schema->nodetype) { |
539 | 0 | case LYS_CONTAINER: |
540 | 0 | case LYS_LIST: |
541 | 0 | case LYS_NOTIF: |
542 | 0 | case LYS_RPC: |
543 | 0 | case LYS_ACTION: |
544 | 0 | ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node); |
545 | 0 | break; |
546 | 0 | case LYS_LEAF: |
547 | 0 | case LYS_LEAFLIST: |
548 | 0 | xml_print_term(ctx, (const struct lyd_node_term *)node); |
549 | 0 | break; |
550 | 0 | case LYS_ANYXML: |
551 | 0 | case LYS_ANYDATA: |
552 | 0 | ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node); |
553 | 0 | break; |
554 | 0 | default: |
555 | 0 | LOGINT(node->schema->module->ctx); |
556 | 0 | ret = LY_EINT; |
557 | 0 | break; |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | | /* remove all added namespaces */ |
562 | 0 | while (ns_count < ctx->ns.count) { |
563 | 0 | lydict_remove(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]); |
564 | 0 | ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL); |
565 | 0 | ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL); |
566 | 0 | } |
567 | |
|
568 | 0 | return ret; |
569 | 0 | } |
570 | | |
571 | | LY_ERR |
572 | | xml_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options) |
573 | 0 | { |
574 | 0 | const struct lyd_node *node; |
575 | 0 | struct xmlpr_ctx ctx = {0}; |
576 | |
|
577 | 0 | if (!root) { |
578 | 0 | if ((out->type == LY_OUT_MEMORY) || (out->type == LY_OUT_CALLBACK)) { |
579 | 0 | ly_print_(out, ""); |
580 | 0 | } |
581 | 0 | goto finish; |
582 | 0 | } |
583 | | |
584 | 0 | ctx.out = out; |
585 | 0 | ctx.level = 0; |
586 | 0 | ctx.options = options; |
587 | 0 | ctx.ctx = LYD_CTX(root); |
588 | | |
589 | | /* content */ |
590 | 0 | LY_LIST_FOR(root, node) { |
591 | 0 | LY_CHECK_RET(xml_print_node(&ctx, node)); |
592 | 0 | if (!(options & LYD_PRINT_WITHSIBLINGS)) { |
593 | 0 | break; |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | 0 | finish: |
598 | 0 | assert(!ctx.prefix.count && !ctx.ns.count); |
599 | 0 | ly_set_erase(&ctx.prefix, NULL); |
600 | | ly_set_erase(&ctx.ns, NULL); |
601 | 0 | ly_print_flush(out); |
602 | 0 | return LY_SUCCESS; |
603 | 0 | } |