blob: d63b16c0a221e6582358c505e0b82f48053103b3 [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
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
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#include <assert.h>
17#include <stdint.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020018#include <stdlib.h>
19#include <string.h>
20
Radek Krejci535ea9f2020-05-29 16:01:05 +020021#include "common.h"
22#include "context.h"
23#include "dict.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include "log.h"
25#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020026#include "printer.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020027#include "printer_data.h"
28#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020030#include "tree.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020031#include "tree_schema.h"
32#include "xml.h"
33
34/**
35 * @brief XML printer context.
36 */
37struct xmlpr_ctx {
Radek Krejci241f6b52020-05-21 18:13:49 +020038 struct ly_out *out; /**< output specification */
Radek Krejcie7b95092019-05-15 11:03:07 +020039 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
40 int options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010041 const struct ly_ctx *ctx; /**< libyang context */
42 struct ly_set prefix; /**< printed namespace prefixes */
43 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020044};
45
46#define LEVEL ctx->level /**< current level */
47#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
48#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
49#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
50
Michal Vasko52927e22020-03-16 17:26:14 +010051#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejcie7b95092019-05-15 11:03:07 +020052
53/**
Michal Vasko52927e22020-03-16 17:26:14 +010054 * @brief Print a namespace if not already printed.
55 *
56 * @param[in] ctx XML printer context.
57 * @param[in] ns Namespace to print, expected to be in dictionary.
58 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
59 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
60 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020061 */
Michal Vasko52927e22020-03-16 17:26:14 +010062static const char *
63xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, int prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020064{
Michal Vasko52927e22020-03-16 17:26:14 +010065 int i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010066
Michal Vasko52927e22020-03-16 17:26:14 +010067 for (i = ctx->ns.count - 1; i > -1; --i) {
68 if (!new_prefix) {
69 /* find default namespace */
70 if (!ctx->prefix.objs[i]) {
71 if (ctx->ns.objs[i] != ns) {
72 /* different default namespace */
73 i = -1;
Radek Krejcie7b95092019-05-15 11:03:07 +020074 }
Michal Vasko52927e22020-03-16 17:26:14 +010075 break;
76 }
77 } else {
78 /* find prefixed namespace */
79 if (ctx->ns.objs[i] == ns) {
80 if (!ctx->prefix.objs[i]) {
81 /* default namespace is not interesting */
82 continue;
83 }
84
85 if (!strcmp(ctx->prefix.objs[i], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
86 /* the same prefix or can be any */
87 break;
88 }
89 }
Radek Krejcie7b95092019-05-15 11:03:07 +020090 }
Radek Krejcie7b95092019-05-15 11:03:07 +020091 }
Radek Krejci28681fa2019-09-06 13:08:45 +020092
Michal Vasko52927e22020-03-16 17:26:14 +010093 if (i == -1) {
94 /* suitable namespace not found, must be printed */
Radek Krejci241f6b52020-05-21 18:13:49 +020095 ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020096
Michal Vasko52927e22020-03-16 17:26:14 +010097 /* and added into namespaces */
98 if (new_prefix) {
99 new_prefix = lydict_insert(ctx->ctx, new_prefix, 0);
100 }
101 ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST);
102 i = ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST);
Radek Krejcie7b95092019-05-15 11:03:07 +0200103 }
Michal Vasko52927e22020-03-16 17:26:14 +0100104
105 /* return it */
106 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200107}
108
109/**
110 * @brief XML mapping of YANG modules to prefixes in values.
111 *
112 * Implementation of ly_clb_get_prefix
113 */
114static const char *
115xml_print_get_prefix(const struct lys_module *mod, void *private)
116{
117 struct ly_set *ns_list = (struct ly_set*)private;
118
119 ly_set_add(ns_list, (void*)mod, 0);
120 return mod->prefix;
Radek Krejcie7b95092019-05-15 11:03:07 +0200121}
122
123/**
124 * TODO
125 */
Michal Vasko52927e22020-03-16 17:26:14 +0100126static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100127xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200128{
Michal Vasko9f96a052020-03-10 09:41:45 +0100129 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100130 const struct lys_module *mod;
131 struct ly_set ns_list = {0};
Radek Krejci28681fa2019-09-06 13:08:45 +0200132#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200133 const char **prefs, **nss;
134 const char *xml_expr = NULL, *mod_name;
135 uint32_t ns_count, i;
136 int rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200137 char *p;
138 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200139#endif
Radek Krejci28681fa2019-09-06 13:08:45 +0200140 int dynamic;
141 unsigned int u;
Radek Krejcie7b95092019-05-15 11:03:07 +0200142
Radek Krejcie7b95092019-05-15 11:03:07 +0200143 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100144 if (node->schema->nodetype & LYD_NODE_TERM) {
145 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
146 ((ctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100147 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
148 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
149 if (mod) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200150 ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200151 }
152 }
153 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100154#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200155 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
156 if (!strcmp(node->schema->name, "filter")
157 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
158 rpc_filter = 1;
159 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200160#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100161 for (meta = node->meta; meta; meta = meta->next) {
162 const char *value = meta->value.realtype->plugin->print(&meta->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejci28681fa2019-09-06 13:08:45 +0200163
Michal Vasko52927e22020-03-16 17:26:14 +0100164 /* print namespaces connected with the value's prefixes */
Radek Krejci28681fa2019-09-06 13:08:45 +0200165 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100166 mod = (const struct lys_module *)ns_list.objs[u];
167 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200168 }
169 ly_set_erase(&ns_list, NULL);
170
171#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200172 if (rpc_filter) {
173 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100174 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200175 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100176 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200177 if (!xml_expr) {
178 /* error */
179 return EXIT_FAILURE;
180 }
181
182 for (i = 0; i < ns_count; ++i) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200183 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200184 }
185 free(prefs);
186 free(nss);
187 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200188 ly_print(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200189 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200190#endif
Michal Vasko52927e22020-03-16 17:26:14 +0100191 /* print the metadata with its namespace */
192 mod = meta->annotation->module;
Radek Krejci241f6b52020-05-21 18:13:49 +0200193 ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200194#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200195 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200196#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200197
Michal Vasko52927e22020-03-16 17:26:14 +0100198 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200199 if (value && value[0]) {
200 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200201 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200202 ly_print(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200203 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100204 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200205 }
206 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200207}
208
209/**
210 * @brief Print generic XML element despite of the data node type.
211 *
212 * Prints the element name, attributes and necessary namespaces.
213 *
214 * @param[in] ctx XML printer context.
215 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200216 */
Michal Vasko52927e22020-03-16 17:26:14 +0100217static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200218xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
219{
Michal Vasko52927e22020-03-16 17:26:14 +0100220 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200221 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100222
223 /* print default namespace */
224 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
225
226 /* print metadata */
227 xml_print_meta(ctx, node);
228}
229
230static LY_ERR
231xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
232{
233 const struct ly_attr *attr;
234 const char *pref;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200235 LY_ARRAY_SIZE_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100236
237 LY_LIST_FOR(node->attr, attr) {
238 pref = NULL;
239 if (attr->prefix.pref) {
240 /* print attribute namespace */
241 switch (attr->format) {
242 case LYD_XML:
243 pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
244 break;
245 case LYD_SCHEMA:
246 /* cannot be created */
247 LOGINT(node->ctx);
248 return LY_EINT;
249 }
250 }
251
252 /* print namespaces connected with the value's prefixes */
253 if (attr->val_prefs) {
254 LY_ARRAY_FOR(attr->val_prefs, u) {
255 xml_print_ns(ctx, attr->val_prefs[u].ns, attr->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
256 }
257 }
258
259 /* print the attribute with its prefix and value */
Radek Krejci241f6b52020-05-21 18:13:49 +0200260 ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200261 }
262
Michal Vasko52927e22020-03-16 17:26:14 +0100263 return LY_SUCCESS;
264}
265
266static LY_ERR
267xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
268{
269 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200270 ly_print(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100271
272 /* print default namespace */
273 switch (node->format) {
274 case LYD_XML:
275 xml_print_ns(ctx, node->prefix.ns, NULL, 0);
276 break;
277 case LYD_SCHEMA:
278 /* cannot be created */
279 LOGINT(node->ctx);
280 return LY_EINT;
Radek Krejcie7b95092019-05-15 11:03:07 +0200281 }
282
Michal Vasko52927e22020-03-16 17:26:14 +0100283 /* print attributes */
284 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200285
286 return LY_SUCCESS;
287}
288
289static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
290
291/**
292 * @brief Print XML element representing lyd_node_term.
293 *
294 * @param[in] ctx XML printer context.
295 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200296 */
Michal Vasko52927e22020-03-16 17:26:14 +0100297static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200298xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
299{
Radek Krejcia1911222019-07-22 17:24:50 +0200300 struct ly_set ns_list = {0};
301 unsigned int u;
302 int dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200303 const char *value;
304
Michal Vasko52927e22020-03-16 17:26:14 +0100305 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100306 value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
Radek Krejcie7b95092019-05-15 11:03:07 +0200307
Radek Krejcia1911222019-07-22 17:24:50 +0200308 /* print namespaces connected with the values's prefixes */
309 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100310 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Radek Krejci241f6b52020-05-21 18:13:49 +0200311 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200312 }
Radek Krejcia1911222019-07-22 17:24:50 +0200313 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200314
Radek Krejcia1911222019-07-22 17:24:50 +0200315 if (!value || !value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200316 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200317 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200318 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200319 lyxml_dump_text(ctx->out, value, 0);
Radek Krejci241f6b52020-05-21 18:13:49 +0200320 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200321 }
Radek Krejcia1911222019-07-22 17:24:50 +0200322 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100323 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200324 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200325}
326
327/**
328 * @brief Print XML element representing lyd_node_inner.
329 *
330 * @param[in] ctx XML printer context.
331 * @param[in] node Data node to be printed.
332 * @return LY_ERR value.
333 */
334static LY_ERR
335xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
336{
337 LY_ERR ret;
338 struct lyd_node *child;
339
Michal Vasko52927e22020-03-16 17:26:14 +0100340 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200341
342 if (!node->child) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200343 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200344 return LY_SUCCESS;
345 }
346
347 /* children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200348 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200349
350 LEVEL_INC;
351 LY_LIST_FOR(node->child, child) {
352 ret = xml_print_node(ctx, child);
353 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
354 }
355 LEVEL_DEC;
356
Radek Krejci241f6b52020-05-21 18:13:49 +0200357 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200358
359 return LY_SUCCESS;
360}
361
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200362static LY_ERR
363xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200364{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200365 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200366 struct lyd_node *iter;
Michal Vasko52927e22020-03-16 17:26:14 +0100367 int prev_opts;
368 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200369
Michal Vasko52927e22020-03-16 17:26:14 +0100370 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200371
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200372 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200373 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200374no_content:
Radek Krejci241f6b52020-05-21 18:13:49 +0200375 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200376 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200377 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200378 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200379 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200380 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100381 prev_opts = ctx->options;
Michal Vasko1ce933a2020-03-30 12:38:22 +0200382 ctx->options &= ~LYDP_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200383 LEVEL_INC;
384
Radek Krejci241f6b52020-05-21 18:13:49 +0200385 ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200386 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100387 ret = xml_print_node(ctx, iter);
388 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200389 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200390
391 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100392 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200393 break;
394 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200395 /* escape XML-sensitive characters */
396 if (!any->value.str[0]) {
397 goto no_content;
398 }
399 /* close opening tag and print data */
Radek Krejci241f6b52020-05-21 18:13:49 +0200400 ly_print(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200401 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200402 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200403 case LYD_ANYDATA_XML:
404 /* print without escaping special characters */
405 if (!any->value.str[0]) {
406 goto no_content;
407 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200408 ly_print(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200409 break;
410 case LYD_ANYDATA_JSON:
411#if 0 /* TODO LYB format */
412 case LYD_ANYDATA_LYB:
413#endif
414 /* JSON and LYB format is not supported */
415 LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
416 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200417 }
418
419 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200420 if (any->value_type == LYD_ANYDATA_DATATREE) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200421 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200422 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200423 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200424 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200425 }
426
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200427 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200428}
Radek Krejcie7b95092019-05-15 11:03:07 +0200429
Michal Vasko52927e22020-03-16 17:26:14 +0100430static LY_ERR
431xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
432{
433 LY_ERR ret;
434 struct lyd_node *child;
Radek Krejci7eb54ba2020-05-18 16:30:04 +0200435 LY_ARRAY_SIZE_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100436
437 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
438
439 if (node->value[0]) {
440 /* print namespaces connected with the value's prefixes */
441 if (node->val_prefs) {
442 LY_ARRAY_FOR(node->val_prefs, u) {
443 xml_print_ns(ctx, node->val_prefs[u].ns, node->val_prefs[u].pref, LYXML_PREFIX_REQUIRED);
444 }
445 }
446
Radek Krejci241f6b52020-05-21 18:13:49 +0200447 ly_print(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100448 }
449
450 if (node->child) {
451 /* children */
452 if (!node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200453 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100454 }
455
456 LEVEL_INC;
457 LY_LIST_FOR(node->child, child) {
458 ret = xml_print_node(ctx, child);
459 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
460 }
461 LEVEL_DEC;
462
Radek Krejci241f6b52020-05-21 18:13:49 +0200463 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100464 } else if (node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200465 ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100466 } else {
467 /* no value or children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200468 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100469 }
470
471 return LY_SUCCESS;
472}
473
Radek Krejcie7b95092019-05-15 11:03:07 +0200474/**
475 * @brief Print XML element representing lyd_node.
476 *
477 * @param[in] ctx XML printer context.
478 * @param[in] node Data node to be printed.
479 * @return LY_ERR value.
480 */
481static LY_ERR
482xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
483{
484 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100485 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200486
Michal Vasko9b368d32020-02-14 13:53:31 +0100487 if (!ly_should_print(node, ctx->options)) {
488 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100489 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200490 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200491
Michal Vasko52927e22020-03-16 17:26:14 +0100492 /* remember namespace definition count on this level */
493 ns_count = ctx->ns.count;
494
495 if (!node->schema) {
496 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
497 } else {
498 switch (node->schema->nodetype) {
499 case LYS_CONTAINER:
500 case LYS_LIST:
501 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100502 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100503 case LYS_ACTION:
504 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
505 break;
506 case LYS_LEAF:
507 case LYS_LEAFLIST:
508 xml_print_term(ctx, (const struct lyd_node_term *)node);
509 break;
510 case LYS_ANYXML:
511 case LYS_ANYDATA:
512 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
513 break;
514 default:
515 LOGINT(node->schema->module->ctx);
516 ret = LY_EINT;
517 break;
518 }
519 }
520
521 /* remove all added namespaces */
522 while (ns_count < ctx->ns.count) {
523 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
524 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
525 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200526 }
527
528 return ret;
529}
530
531LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200532xml_print_data(struct ly_out *out, const struct lyd_node *root, int options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200533{
534 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100535 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200536
537 if (!root) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200538 if (out->type == LY_OUT_MEMORY || out->type == LY_OUT_CALLBACK) {
539 ly_print(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200540 }
541 goto finish;
542 }
543
Michal Vasko52927e22020-03-16 17:26:14 +0100544 ctx.out = out;
545 ctx.level = (options & LYDP_FORMAT ? 1 : 0);
546 ctx.options = options;
547 ctx.ctx = LYD_NODE_CTX(root);
548
Radek Krejcie7b95092019-05-15 11:03:07 +0200549 /* content */
550 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100551 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200552 if (!(options & LYDP_WITHSIBLINGS)) {
553 break;
554 }
555 }
556
557finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100558 assert(!ctx.prefix.count && !ctx.ns.count);
559 ly_set_erase(&ctx.prefix, NULL);
560 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200561 ly_print_flush(out);
562 return LY_SUCCESS;
563}
564