blob: 3a9440641511df921d2a378be0c83c75d2d6d0d1 [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"
Radek Krejci7931b192020-06-25 17:05:03 +020025#include "parser_data.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include "plugins_types.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020027#include "printer.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020028#include "printer_data.h"
29#include "printer_internal.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "set.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020031#include "tree.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020032#include "tree_schema.h"
33#include "xml.h"
34
35/**
36 * @brief XML printer context.
37 */
38struct xmlpr_ctx {
Radek Krejci241f6b52020-05-21 18:13:49 +020039 struct ly_out *out; /**< output specification */
Radek Krejcie7b95092019-05-15 11:03:07 +020040 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
41 int options; /**< [Data printer flags](@ref dataprinterflags) */
Michal Vasko52927e22020-03-16 17:26:14 +010042 const struct ly_ctx *ctx; /**< libyang context */
43 struct ly_set prefix; /**< printed namespace prefixes */
44 struct ly_set ns; /**< printed namespaces */
Radek Krejcie7b95092019-05-15 11:03:07 +020045};
46
47#define LEVEL ctx->level /**< current level */
48#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
49#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
50#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
51
Michal Vasko52927e22020-03-16 17:26:14 +010052#define LYXML_PREFIX_REQUIRED 0x01 /**< The prefix is not just a suggestion but a requirement. */
Radek Krejci1798aae2020-07-14 13:26:06 +020053#define LYXML_PREFIX_DEFAULT 0x02 /**< The namespace is required to be a default (without prefix) */
Radek Krejcie7b95092019-05-15 11:03:07 +020054
55/**
Michal Vasko52927e22020-03-16 17:26:14 +010056 * @brief Print a namespace if not already printed.
57 *
58 * @param[in] ctx XML printer context.
59 * @param[in] ns Namespace to print, expected to be in dictionary.
60 * @param[in] new_prefix Suggested new prefix, NULL for a default namespace without prefix. Stored in the dictionary.
61 * @param[in] prefix_opts Prefix options changing the meaning of parameters.
62 * @return Printed prefix of the namespace to use.
Radek Krejcie7b95092019-05-15 11:03:07 +020063 */
Michal Vasko52927e22020-03-16 17:26:14 +010064static const char *
65xml_print_ns(struct xmlpr_ctx *ctx, const char *ns, const char *new_prefix, int prefix_opts)
Radek Krejcie7b95092019-05-15 11:03:07 +020066{
Michal Vasko52927e22020-03-16 17:26:14 +010067 int i;
Michal Vasko6f4cbb62020-02-28 11:15:47 +010068
Michal Vasko52927e22020-03-16 17:26:14 +010069 for (i = ctx->ns.count - 1; i > -1; --i) {
70 if (!new_prefix) {
71 /* find default namespace */
72 if (!ctx->prefix.objs[i]) {
73 if (ctx->ns.objs[i] != ns) {
74 /* different default namespace */
75 i = -1;
Radek Krejcie7b95092019-05-15 11:03:07 +020076 }
Michal Vasko52927e22020-03-16 17:26:14 +010077 break;
78 }
79 } else {
80 /* find prefixed namespace */
81 if (ctx->ns.objs[i] == ns) {
82 if (!ctx->prefix.objs[i]) {
83 /* default namespace is not interesting */
84 continue;
85 }
86
87 if (!strcmp(ctx->prefix.objs[i], new_prefix) || !(prefix_opts & LYXML_PREFIX_REQUIRED)) {
88 /* the same prefix or can be any */
89 break;
90 }
91 }
Radek Krejcie7b95092019-05-15 11:03:07 +020092 }
Radek Krejcie7b95092019-05-15 11:03:07 +020093 }
Radek Krejci28681fa2019-09-06 13:08:45 +020094
Michal Vasko52927e22020-03-16 17:26:14 +010095 if (i == -1) {
96 /* suitable namespace not found, must be printed */
Radek Krejci241f6b52020-05-21 18:13:49 +020097 ly_print(ctx->out, " xmlns%s%s=\"%s\"", new_prefix ? ":" : "", new_prefix ? new_prefix : "", ns);
Radek Krejcie7b95092019-05-15 11:03:07 +020098
Michal Vasko52927e22020-03-16 17:26:14 +010099 /* and added into namespaces */
100 if (new_prefix) {
101 new_prefix = lydict_insert(ctx->ctx, new_prefix, 0);
102 }
103 ly_set_add(&ctx->prefix, (void *)new_prefix, LY_SET_OPT_USEASLIST);
104 i = ly_set_add(&ctx->ns, (void *)ns, LY_SET_OPT_USEASLIST);
Radek Krejcie7b95092019-05-15 11:03:07 +0200105 }
Michal Vasko52927e22020-03-16 17:26:14 +0100106
107 /* return it */
108 return ctx->prefix.objs[i];
Radek Krejci28681fa2019-09-06 13:08:45 +0200109}
110
Radek Krejci1798aae2020-07-14 13:26:06 +0200111static const char*
112xml_print_ns_opaq(struct xmlpr_ctx *ctx, LYD_FORMAT format, const struct ly_prefix *prefix, int prefix_opts)
113{
114
115 switch (format) {
116 case LYD_XML:
117 return xml_print_ns(ctx, prefix->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
118 break;
119 case LYD_JSON:
120 if (prefix->module_name) {
121 const struct lys_module *mod = ly_ctx_get_module_latest(ctx->ctx, prefix->module_name);
122 if (mod) {
123 return xml_print_ns(ctx, mod->ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : prefix->id, prefix_opts);
124 }
125 }
126 break;
127 case LYD_SCHEMA:
128 case LYD_LYB:
129 /* cannot be created */
130 LOGINT(ctx->ctx);
131 }
132
133 return NULL;
134}
135
Radek Krejci28681fa2019-09-06 13:08:45 +0200136/**
137 * @brief XML mapping of YANG modules to prefixes in values.
138 *
Radek Krejci1798aae2020-07-14 13:26:06 +0200139 * Implementation of ly_get_prefix_clb
Radek Krejci28681fa2019-09-06 13:08:45 +0200140 */
141static const char *
142xml_print_get_prefix(const struct lys_module *mod, void *private)
143{
144 struct ly_set *ns_list = (struct ly_set*)private;
145
146 ly_set_add(ns_list, (void*)mod, 0);
147 return mod->prefix;
Radek Krejcie7b95092019-05-15 11:03:07 +0200148}
149
150/**
151 * TODO
152 */
Michal Vasko52927e22020-03-16 17:26:14 +0100153static void
Michal Vasko9f96a052020-03-10 09:41:45 +0100154xml_print_meta(struct xmlpr_ctx *ctx, const struct lyd_node *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200155{
Michal Vasko9f96a052020-03-10 09:41:45 +0100156 struct lyd_meta *meta;
Michal Vasko52927e22020-03-16 17:26:14 +0100157 const struct lys_module *mod;
158 struct ly_set ns_list = {0};
Radek Krejci28681fa2019-09-06 13:08:45 +0200159#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200160 const char **prefs, **nss;
161 const char *xml_expr = NULL, *mod_name;
162 uint32_t ns_count, i;
163 int rpc_filter = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200164 char *p;
165 size_t len;
Radek Krejci28681fa2019-09-06 13:08:45 +0200166#endif
Radek Krejci28681fa2019-09-06 13:08:45 +0200167 int dynamic;
168 unsigned int u;
Radek Krejcie7b95092019-05-15 11:03:07 +0200169
Radek Krejcie7b95092019-05-15 11:03:07 +0200170 /* with-defaults */
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100171 if (node->schema->nodetype & LYD_NODE_TERM) {
Radek Krejci7931b192020-06-25 17:05:03 +0200172 if (((node->flags & LYD_DEFAULT) && (ctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) ||
173 ((ctx->options & LYD_PRINT_WD_ALL_TAG) && ly_is_default(node))) {
Michal Vasko52927e22020-03-16 17:26:14 +0100174 /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
175 mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
176 if (mod) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200177 ly_print(ctx->out, " %s:default=\"true\"", xml_print_ns(ctx, mod->ns, mod->prefix, 0));
Radek Krejcie7b95092019-05-15 11:03:07 +0200178 }
179 }
180 }
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100181#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200182 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
183 if (!strcmp(node->schema->name, "filter")
184 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
185 rpc_filter = 1;
186 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200187#endif
Michal Vasko9f96a052020-03-10 09:41:45 +0100188 for (meta = node->meta; meta; meta = meta->next) {
189 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 +0200190
Michal Vasko52927e22020-03-16 17:26:14 +0100191 /* print namespaces connected with the value's prefixes */
Radek Krejci28681fa2019-09-06 13:08:45 +0200192 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko52927e22020-03-16 17:26:14 +0100193 mod = (const struct lys_module *)ns_list.objs[u];
194 xml_print_ns(ctx, mod->ns, mod->prefix, 1);
Radek Krejci28681fa2019-09-06 13:08:45 +0200195 }
196 ly_set_erase(&ns_list, NULL);
197
198#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200199 if (rpc_filter) {
200 /* exception for NETCONF's filter's attributes */
Michal Vasko9f96a052020-03-10 09:41:45 +0100201 if (!strcmp(meta->name, "select")) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200202 /* xpath content, we have to convert the JSON format into XML first */
Michal Vasko9f96a052020-03-10 09:41:45 +0100203 xml_expr = transform_json2xml(node->schema->module, meta->value_str, 0, &prefs, &nss, &ns_count);
Radek Krejcie7b95092019-05-15 11:03:07 +0200204 if (!xml_expr) {
205 /* error */
206 return EXIT_FAILURE;
207 }
208
209 for (i = 0; i < ns_count; ++i) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200210 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
Radek Krejcie7b95092019-05-15 11:03:07 +0200211 }
212 free(prefs);
213 free(nss);
214 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200215 ly_print(out, " %s=\"", meta->name);
Radek Krejcie7b95092019-05-15 11:03:07 +0200216 } else {
Radek Krejci28681fa2019-09-06 13:08:45 +0200217#endif
Michal Vasko52927e22020-03-16 17:26:14 +0100218 /* print the metadata with its namespace */
219 mod = meta->annotation->module;
Radek Krejci241f6b52020-05-21 18:13:49 +0200220 ly_print(ctx->out, " %s:%s=\"", xml_print_ns(ctx, mod->ns, mod->prefix, 1), meta->name);
Radek Krejci28681fa2019-09-06 13:08:45 +0200221#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200222 }
Radek Krejci28681fa2019-09-06 13:08:45 +0200223#endif
Radek Krejcie7b95092019-05-15 11:03:07 +0200224
Michal Vasko52927e22020-03-16 17:26:14 +0100225 /* print metadata value */
Radek Krejci28681fa2019-09-06 13:08:45 +0200226 if (value && value[0]) {
227 lyxml_dump_text(ctx->out, value, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200228 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200229 ly_print(ctx->out, "\"");
Radek Krejci28681fa2019-09-06 13:08:45 +0200230 if (dynamic) {
Michal Vasko52927e22020-03-16 17:26:14 +0100231 free((void *)value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200232 }
233 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200234}
235
236/**
237 * @brief Print generic XML element despite of the data node type.
238 *
239 * Prints the element name, attributes and necessary namespaces.
240 *
241 * @param[in] ctx XML printer context.
242 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200243 */
Michal Vasko52927e22020-03-16 17:26:14 +0100244static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200245xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
246{
Michal Vasko52927e22020-03-16 17:26:14 +0100247 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200248 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100249
250 /* print default namespace */
251 xml_print_ns(ctx, node->schema->module->ns, NULL, 0);
252
253 /* print metadata */
254 xml_print_meta(ctx, node);
255}
256
257static LY_ERR
258xml_print_attr(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
259{
260 const struct ly_attr *attr;
261 const char *pref;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200262 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100263
264 LY_LIST_FOR(node->attr, attr) {
265 pref = NULL;
Radek Krejci1798aae2020-07-14 13:26:06 +0200266 if (attr->prefix.id) {
Michal Vasko52927e22020-03-16 17:26:14 +0100267 /* print attribute namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200268 pref = xml_print_ns_opaq(ctx, attr->format, &attr->prefix, 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100269 }
270
271 /* print namespaces connected with the value's prefixes */
272 if (attr->val_prefs) {
273 LY_ARRAY_FOR(attr->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200274 xml_print_ns_opaq(ctx, attr->format, &attr->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100275 }
276 }
277
278 /* print the attribute with its prefix and value */
Radek Krejci241f6b52020-05-21 18:13:49 +0200279 ly_print(ctx->out, " %s%s%s=\"%s\"", pref ? pref : "", pref ? ":" : "", attr->name, attr->value);
Radek Krejcie7b95092019-05-15 11:03:07 +0200280 }
281
Michal Vasko52927e22020-03-16 17:26:14 +0100282 return LY_SUCCESS;
283}
284
285static LY_ERR
286xml_print_opaq_open(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
287{
288 /* print node name */
Radek Krejci241f6b52020-05-21 18:13:49 +0200289 ly_print(ctx->out, "%*s<%s", INDENT, node->name);
Michal Vasko52927e22020-03-16 17:26:14 +0100290
291 /* print default namespace */
Radek Krejci1798aae2020-07-14 13:26:06 +0200292 xml_print_ns_opaq(ctx, node->format, &node->prefix, LYXML_PREFIX_DEFAULT);
Radek Krejcie7b95092019-05-15 11:03:07 +0200293
Michal Vasko52927e22020-03-16 17:26:14 +0100294 /* print attributes */
295 LY_CHECK_RET(xml_print_attr(ctx, node));
Radek Krejcie7b95092019-05-15 11:03:07 +0200296
297 return LY_SUCCESS;
298}
299
300static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
301
302/**
303 * @brief Print XML element representing lyd_node_term.
304 *
305 * @param[in] ctx XML printer context.
306 * @param[in] node Data node to be printed.
Radek Krejcie7b95092019-05-15 11:03:07 +0200307 */
Michal Vasko52927e22020-03-16 17:26:14 +0100308static void
Radek Krejcie7b95092019-05-15 11:03:07 +0200309xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
310{
Radek Krejcia1911222019-07-22 17:24:50 +0200311 struct ly_set ns_list = {0};
312 unsigned int u;
313 int dynamic;
Radek Krejci28681fa2019-09-06 13:08:45 +0200314 const char *value;
315
Michal Vasko52927e22020-03-16 17:26:14 +0100316 xml_print_node_open(ctx, (struct lyd_node *)node);
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100317 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 +0200318
Radek Krejcia1911222019-07-22 17:24:50 +0200319 /* print namespaces connected with the values's prefixes */
320 for (u = 0; u < ns_list.count; ++u) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100321 const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
Radek Krejci241f6b52020-05-21 18:13:49 +0200322 ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
Radek Krejcie7b95092019-05-15 11:03:07 +0200323 }
Radek Krejcia1911222019-07-22 17:24:50 +0200324 ly_set_erase(&ns_list, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200325
Radek Krejcia1911222019-07-22 17:24:50 +0200326 if (!value || !value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200327 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200328 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200329 ly_print(ctx->out, ">");
Radek Krejcia1911222019-07-22 17:24:50 +0200330 lyxml_dump_text(ctx->out, value, 0);
Radek Krejci241f6b52020-05-21 18:13:49 +0200331 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200332 }
Radek Krejcia1911222019-07-22 17:24:50 +0200333 if (dynamic) {
Michal Vasko6f4cbb62020-02-28 11:15:47 +0100334 free((void *)value);
Radek Krejcia1911222019-07-22 17:24:50 +0200335 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200336}
337
338/**
339 * @brief Print XML element representing lyd_node_inner.
340 *
341 * @param[in] ctx XML printer context.
342 * @param[in] node Data node to be printed.
343 * @return LY_ERR value.
344 */
345static LY_ERR
346xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
347{
348 LY_ERR ret;
349 struct lyd_node *child;
350
Michal Vasko52927e22020-03-16 17:26:14 +0100351 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200352
353 if (!node->child) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200354 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200355 return LY_SUCCESS;
356 }
357
358 /* children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200359 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200360
361 LEVEL_INC;
362 LY_LIST_FOR(node->child, child) {
363 ret = xml_print_node(ctx, child);
364 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
365 }
366 LEVEL_DEC;
367
Radek Krejci241f6b52020-05-21 18:13:49 +0200368 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200369
370 return LY_SUCCESS;
371}
372
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200373static LY_ERR
374xml_print_anydata(struct xmlpr_ctx *ctx, const struct lyd_node_any *node)
Radek Krejcie7b95092019-05-15 11:03:07 +0200375{
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200376 struct lyd_node_any *any = (struct lyd_node_any *)node;
Radek Krejcie7b95092019-05-15 11:03:07 +0200377 struct lyd_node *iter;
Michal Vasko60ea6352020-06-29 13:39:39 +0200378 int prev_opts, prev_lo;
Michal Vasko52927e22020-03-16 17:26:14 +0100379 LY_ERR ret;
Radek Krejcie7b95092019-05-15 11:03:07 +0200380
Michal Vasko52927e22020-03-16 17:26:14 +0100381 xml_print_node_open(ctx, (struct lyd_node *)node);
Radek Krejcie7b95092019-05-15 11:03:07 +0200382
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200383 if (!any->value.tree) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200384 /* no content */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200385no_content:
Radek Krejci241f6b52020-05-21 18:13:49 +0200386 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200387 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200388 } else {
Michal Vasko60ea6352020-06-29 13:39:39 +0200389 if (any->value_type == LYD_ANYDATA_LYB) {
390 /* turn logging off */
391 prev_lo = ly_log_options(0);
392
393 /* try to parse it into a data tree */
Radek Krejci7931b192020-06-25 17:05:03 +0200394 if (lyd_parse_data_mem((struct ly_ctx *)LYD_NODE_CTX(node), any->value.mem, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT, 0, &iter) == LY_SUCCESS) {
Michal Vasko60ea6352020-06-29 13:39:39 +0200395 /* successfully parsed */
396 free(any->value.mem);
397 any->value.tree = iter;
398 any->value_type = LYD_ANYDATA_DATATREE;
399 }
Radek Krejci7931b192020-06-25 17:05:03 +0200400
401 /* turn loggin on again */
402 ly_log_options(prev_lo);
Michal Vasko60ea6352020-06-29 13:39:39 +0200403 }
404
Radek Krejcie7b95092019-05-15 11:03:07 +0200405 switch (any->value_type) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200406 case LYD_ANYDATA_DATATREE:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200407 /* close opening tag and print data */
Michal Vasko52927e22020-03-16 17:26:14 +0100408 prev_opts = ctx->options;
Radek Krejci7931b192020-06-25 17:05:03 +0200409 ctx->options &= ~LYD_PRINT_WITHSIBLINGS;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200410 LEVEL_INC;
411
Radek Krejci241f6b52020-05-21 18:13:49 +0200412 ly_print(ctx->out, ">%s", LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200413 LY_LIST_FOR(any->value.tree, iter) {
Michal Vasko52927e22020-03-16 17:26:14 +0100414 ret = xml_print_node(ctx, iter);
415 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
Radek Krejcie7b95092019-05-15 11:03:07 +0200416 }
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200417
418 LEVEL_DEC;
Michal Vasko52927e22020-03-16 17:26:14 +0100419 ctx->options = prev_opts;
Radek Krejcie7b95092019-05-15 11:03:07 +0200420 break;
421 case LYD_ANYDATA_STRING:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200422 /* escape XML-sensitive characters */
423 if (!any->value.str[0]) {
424 goto no_content;
425 }
426 /* close opening tag and print data */
Radek Krejci241f6b52020-05-21 18:13:49 +0200427 ly_print(ctx->out, ">");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200428 lyxml_dump_text(ctx->out, any->value.str, 0);
Radek Krejcie7b95092019-05-15 11:03:07 +0200429 break;
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200430 case LYD_ANYDATA_XML:
431 /* print without escaping special characters */
432 if (!any->value.str[0]) {
433 goto no_content;
434 }
Radek Krejci241f6b52020-05-21 18:13:49 +0200435 ly_print(ctx->out, ">%s", any->value.str);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200436 break;
437 case LYD_ANYDATA_JSON:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200438 case LYD_ANYDATA_LYB:
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200439 /* JSON and LYB format is not supported */
Michal Vasko60ea6352020-06-29 13:39:39 +0200440 LOGWRN(LYD_NODE_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200441 goto no_content;
Radek Krejcie7b95092019-05-15 11:03:07 +0200442 }
443
444 /* closing tag */
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200445 if (any->value_type == LYD_ANYDATA_DATATREE) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200446 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200447 } else {
Radek Krejci241f6b52020-05-21 18:13:49 +0200448 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200449 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200450 }
451
Radek Krejci26a5dfb2019-07-26 14:51:06 +0200452 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200453}
Radek Krejcie7b95092019-05-15 11:03:07 +0200454
Michal Vasko52927e22020-03-16 17:26:14 +0100455static LY_ERR
456xml_print_opaq(struct xmlpr_ctx *ctx, const struct lyd_node_opaq *node)
457{
458 LY_ERR ret;
459 struct lyd_node *child;
Michal Vaskofd69e1d2020-07-03 11:57:17 +0200460 LY_ARRAY_COUNT_TYPE u;
Michal Vasko52927e22020-03-16 17:26:14 +0100461
462 LY_CHECK_RET(xml_print_opaq_open(ctx, node));
463
464 if (node->value[0]) {
465 /* print namespaces connected with the value's prefixes */
466 if (node->val_prefs) {
467 LY_ARRAY_FOR(node->val_prefs, u) {
Radek Krejci1798aae2020-07-14 13:26:06 +0200468 xml_print_ns_opaq(ctx, node->format, &node->val_prefs[u], LYXML_PREFIX_REQUIRED);
Michal Vasko52927e22020-03-16 17:26:14 +0100469 }
470 }
471
Radek Krejci241f6b52020-05-21 18:13:49 +0200472 ly_print(ctx->out, ">%s", node->value);
Michal Vasko52927e22020-03-16 17:26:14 +0100473 }
474
475 if (node->child) {
476 /* children */
477 if (!node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200478 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100479 }
480
481 LEVEL_INC;
482 LY_LIST_FOR(node->child, child) {
483 ret = xml_print_node(ctx, child);
484 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
485 }
486 LEVEL_DEC;
487
Radek Krejci241f6b52020-05-21 18:13:49 +0200488 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100489 } else if (node->value[0]) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200490 ly_print(ctx->out, "</%s>%s", node->name, LEVEL ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100491 } else {
492 /* no value or children */
Radek Krejci241f6b52020-05-21 18:13:49 +0200493 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
Michal Vasko52927e22020-03-16 17:26:14 +0100494 }
495
496 return LY_SUCCESS;
497}
498
Radek Krejcie7b95092019-05-15 11:03:07 +0200499/**
500 * @brief Print XML element representing lyd_node.
501 *
502 * @param[in] ctx XML printer context.
503 * @param[in] node Data node to be printed.
504 * @return LY_ERR value.
505 */
506static LY_ERR
507xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
508{
509 LY_ERR ret = LY_SUCCESS;
Michal Vasko52927e22020-03-16 17:26:14 +0100510 uint32_t ns_count;
Radek Krejcie7b95092019-05-15 11:03:07 +0200511
Michal Vasko9b368d32020-02-14 13:53:31 +0100512 if (!ly_should_print(node, ctx->options)) {
513 /* do not print at all */
Michal Vasko52927e22020-03-16 17:26:14 +0100514 return LY_SUCCESS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200515 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200516
Michal Vasko52927e22020-03-16 17:26:14 +0100517 /* remember namespace definition count on this level */
518 ns_count = ctx->ns.count;
519
520 if (!node->schema) {
521 ret = xml_print_opaq(ctx, (const struct lyd_node_opaq *)node);
522 } else {
523 switch (node->schema->nodetype) {
524 case LYS_CONTAINER:
525 case LYS_LIST:
526 case LYS_NOTIF:
Michal Vasko1bf09392020-03-27 12:38:10 +0100527 case LYS_RPC:
Michal Vasko52927e22020-03-16 17:26:14 +0100528 case LYS_ACTION:
529 ret = xml_print_inner(ctx, (const struct lyd_node_inner *)node);
530 break;
531 case LYS_LEAF:
532 case LYS_LEAFLIST:
533 xml_print_term(ctx, (const struct lyd_node_term *)node);
534 break;
535 case LYS_ANYXML:
536 case LYS_ANYDATA:
537 ret = xml_print_anydata(ctx, (const struct lyd_node_any *)node);
538 break;
539 default:
540 LOGINT(node->schema->module->ctx);
541 ret = LY_EINT;
542 break;
543 }
544 }
545
546 /* remove all added namespaces */
547 while (ns_count < ctx->ns.count) {
548 FREE_STRING(ctx->ctx, ctx->prefix.objs[ctx->prefix.count - 1]);
549 ly_set_rm_index(&ctx->prefix, ctx->prefix.count - 1, NULL);
550 ly_set_rm_index(&ctx->ns, ctx->ns.count - 1, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200551 }
552
553 return ret;
554}
555
556LY_ERR
Radek Krejci241f6b52020-05-21 18:13:49 +0200557xml_print_data(struct ly_out *out, const struct lyd_node *root, int options)
Radek Krejcie7b95092019-05-15 11:03:07 +0200558{
559 const struct lyd_node *node;
Michal Vasko52927e22020-03-16 17:26:14 +0100560 struct xmlpr_ctx ctx = {0};
Radek Krejcie7b95092019-05-15 11:03:07 +0200561
562 if (!root) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200563 if (out->type == LY_OUT_MEMORY || out->type == LY_OUT_CALLBACK) {
564 ly_print(out, "");
Radek Krejcie7b95092019-05-15 11:03:07 +0200565 }
566 goto finish;
567 }
568
Michal Vasko52927e22020-03-16 17:26:14 +0100569 ctx.out = out;
Radek Krejci7931b192020-06-25 17:05:03 +0200570 ctx.level = (options & LYD_PRINT_FORMAT ? 1 : 0);
Michal Vasko52927e22020-03-16 17:26:14 +0100571 ctx.options = options;
572 ctx.ctx = LYD_NODE_CTX(root);
573
Radek Krejcie7b95092019-05-15 11:03:07 +0200574 /* content */
575 LY_LIST_FOR(root, node) {
Michal Vasko52927e22020-03-16 17:26:14 +0100576 LY_CHECK_RET(xml_print_node(&ctx, node));
Radek Krejci7931b192020-06-25 17:05:03 +0200577 if (!(options & LYD_PRINT_WITHSIBLINGS)) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200578 break;
579 }
580 }
581
582finish:
Michal Vasko52927e22020-03-16 17:26:14 +0100583 assert(!ctx.prefix.count && !ctx.ns.count);
584 ly_set_erase(&ctx.prefix, NULL);
585 ly_set_erase(&ctx.ns, NULL);
Radek Krejcie7b95092019-05-15 11:03:07 +0200586 ly_print_flush(out);
587 return LY_SUCCESS;
588}
589