Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Variant/XML/Writer.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_DataExchange_Variant_XML_Writer_h_
5#define _Stroika_Foundation_DataExchange_Variant_XML_Writer_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
10#include "Stroika/Foundation/Common/Common.h"
15
17
18/**
19 * \file
20 *
21 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
22 *
23 * TODO:
24 * @todo The XML declaration (which the byte overload writes, and the Character overload does not) and its character
25 * set (UTF-8) as Options too, if ever wanted.
26 */
27
28namespace Stroika::Foundation::DataExchange::Variant::XML {
29
30 using Characters::String;
31
32 /**
33 * \brief Write a VariantValue as XML - an object's member is an element, an array is repeated elements
34 *
35 * | VariantValue | XML |
36 * |-------------------------------|-----|
37 * | an object's member K | the element `<K>` |
38 * | an array under member K | `<K>`, wrapping an `<Array>` per item - or as the Binding's fItems or fDefaultArrayItems say: items of another name, or `<K>` repeated |
39 * | anything else | its element's text, as VariantValue::As<String> writes it (a date ISO 8601, a BLOB base64) |
40 * | null, or an empty object | an empty element |
41 *
42 * \par Example Usage
43 * \code
44 * // v is {"specVersion": {"major": 1}, "serviceList": {"service": ["a", "b"]}}
45 * // <service> repeated, not <Array>s
46 * Binding device{.fName = "device"sv, .fDefaultArrayItems = make_shared<Binding> (Binding::kRepeatedArrayItems)};
47 * Variant::XML::Writer{{.fBinding = device}}.WriteAsString (v);
48 * \endcode
49 * \code
50 * <device>
51 * <serviceList>
52 * <service>a</service>
53 * <service>b</service>
54 * </serviceList>
55 * <specVersion>
56 * <major>1</major>
57 * </specVersion>
58 * </device>
59 * \endcode
60 * Members come out in whatever order the value's Mapping gives them (here serviceList first), a VariantValue object
61 * having no order of its own. A Binding gives them one (UPnP wants specVersion first).
62 *
63 * \note The byte overload of Write writes a document, so it begins with an XML declaration naming the encoding it writes
64 * (UTF-8); the Character overload writes none, the caller owning the encoding, and perhaps writing this into a
65 * document of its own.
66 *
67 * That is the shape with nothing declared. Where it is not the shape wanted - an attribute rather than an element, a
68 * namespace, a name of its own, an order of its own (XML cares about the order of an element's children, and a
69 * VariantValue object has none) - say so in a Binding, which the Reader needs as much as the Writer. @see Binding
70 * A member keyed "Array" needs one too, naming it, or it reads back as an array's item (@see Binding::fDefaultArrayItems).
71 *
72 * **A Binding that declares a value's type (Binding::fType) writes it very differently** (fType's table): only a value of
73 * that type - anything else throws - an object only with the members its Binding declares, and a null member as none at
74 * all. With no type declared, any value is written, as it is.
75 *
76 * **Given a complete Binding (Binding::IsComplete), the Reader reads back a value equal to what this wrote
77 * (VariantValue::operator==: the same members, items and types, and Strings of the same code points) - or this throws
78 * BadFormatException, v not being one the Binding describes: never anything in between** (but for what XML writes as
79 * nothing - @see Binding::IsComplete). The document is not promised: this picks one spelling of each character - the
80 * character itself, or a character reference - and any other spelling reads the same (U+1F600 written literally, as
81 * `&#128512;` or as `&#x1F600;` is one character).
82 *
83 * \note A namespace is written as a default namespace (xmlns="..."), inherited by the elements inside it - or with a
84 * prefix, where a Binding's fNamespacePrefixes gives it one, which an attribute in a namespace requires. Mixed
85 * content, comments and processing instructions are still not expressible (and SAX reports no comment, so none
86 * could be read back anyway).
87 * @see https://github.com/SophistSolutions/Stroika/issues/954
88 *
89 * \note Throws DataExchange::BadFormatException where v cannot be written as XML - v is data, not code:
90 * - a name that is not an XML name: a key such as "my key" or "1a", or a Binding's name or prefix
91 * - no single document element, a document having exactly one: with no Binding::fName, a v that is not an object of
92 * exactly one member (that member is the document element); or a document element that would be repeated, for an
93 * array of items with no name of their own
94 * - a value of another type than its Binding declares (Binding::fType), or a member of an object its Binding
95 * does not declare
96 * - a character XML 1.0 cannot hold at all, not even as a character reference: U+0000 to U+001F but TAB, LF and
97 * CR, U+FFFE, U+FFFF
98 * - a text member (Binding::Kind::eText) beside child elements: mixed content, which the Reader cannot read back
99 */
100 class Writer : public Variant::Writer {
101 private:
103
104 private:
105 class Rep_;
106
107 public:
108 /**
109 */
110 struct Options {
111 /**
112 * \brief How the value differs from the default shape - so the Reader must be given the same, to read it back
113 */
114 Binding fBinding;
115
116 /**
117 * \brief An element to a line, indented by its depth; false: no whitespace between elements - nor after the XML
118 * declaration, nor at the end
119 */
120 bool fPrettyPrint{true};
121
122 /**
123 * \brief Spaces per level of indentation - ignored unless fPrettyPrint
124 */
125 unsigned int fSpacesPerIndent{4};
126 };
127
128 public:
129#if qCompilerAndStdLib_default_member_initializer_needed_within_enclosing_class_Buggy
130 Writer ();
131 Writer (const Options& options); ///< \brief Writing as options says
132#else
133 Writer (const Options& options = {}); ///< \brief Writing as options says
134#endif
135 };
136
137}
138
139/*
140 ********************************************************************************
141 ***************************** Implementation Details ***************************
142 ********************************************************************************
143 */
144#include "Writer.inl"
145
146#endif /*_Stroika_Foundation_DataExchange_Variant_XML_Writer_h_*/
abstract class specifying interface for writers VariantValue objects to serialized formats like JSON,...
Write a VariantValue as XML - an object's member is an element, an array is repeated elements.
Writer(const Options &options={})
Writing as options says.
How a VariantValue is to be written as XML, and read back, where the default shape (.
Definition Binding.h:88