Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Binding.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_Binding_h_
5#define _Stroika_Foundation_DataExchange_Variant_XML_Binding_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <memory>
10
12#include "Stroika/Foundation/Containers/Mapping.h"
13#include "Stroika/Foundation/Containers/Sequence.h"
15#include "Stroika/Foundation/DataExchange/XML/Namespace.h"
17
18/**
19 * \file
20 *
21 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
22 */
23
24namespace Stroika::Foundation::DataExchange::Variant::XML {
25
26 using Characters::String;
27 using Containers::Mapping;
28 using Containers::Sequence;
29 using DataExchange::XML::NameWithNamespace;
30 using IO::Network::URI;
31
32 /**
33 * \brief How a VariantValue is to be written as XML, and read back, where the default shape (@see Variant::XML::Writer) is
34 * not what is wanted (e.g. an attribute rather than an element, a namespace, a name of its own, an order of its own)
35 *
36 * A Binding mirrors the shape of the value it goes with: an object's members are fMembers, **in the order XML wants them**
37 * (an element's children are an ordered list, and xs:sequence is XSD's normal idiom, while a VariantValue object has no
38 * order); each item of an array is fItems. Nothing is required - a member fMembers does not name keeps the default shape,
39 * and is written after those it does - so a Binding need say only how its value differs.
40 *
41 * **Declaring a value's type (fType) changes what the Writer and Reader do with it, a great deal** - fType's table says
42 * how: with no type declared they take any value, and guess at any document; with one, they read back that type, and throw
43 * on anything else.
44 *
45 * **Given a complete Binding (IsComplete), the Writer and Reader round-trip a VariantValue faithfully, or throw
46 * BadFormatException because the value is not one the Binding describes - never anything in between**: nothing guessed,
47 * nothing silently lost, whatever values it holds - as an XSD or a WSDL does. A complete Binding declares every value's
48 * type, and leaves nothing to the default shape. The one exception is what XML writes as nothing, read back absent (@see
49 * IsComplete). A Binding that leaves things to defaults is shorter, and particular values can then collide with them (a
50 * member keyed "Array" reads back as an array's item) - the price of the short Binding, not a bug.
51 *
52 * **The top level Binding** - the one a Reader or Writer is given - describes the whole document; those inside it (fMembers,
53 * fItems) each describe a part of it. With an fName, the top level Binding names the document element, and the value is its
54 * content (reading a document element of another name throws BadFormatException). With none, the value is an object of
55 * exactly one member, and that member is the document element - named, and bound, as any other member is (writing any other
56 * value throws BadFormatException).
57 *
58 * Anything may produce a Binding: written by hand, as below; generated from an ObjectVariantMapper, which knows a type's field
59 * names and their declaration order (not yet - https://github.com/SophistSolutions/Stroika/issues/1230); or taken from a
60 * schema of someone else's, an XSD or a WSDL, where the order is normative rather than incidental.
61 *
62 * \par Example Usage
63 * \code
64 * // UPnP's <stateVariable>: sendEvents an attribute, and name before dataType (a VariantValue object has no order)
65 * VariantValue v{Mapping<String, VariantValue>{{"name"sv, VariantValue{"Status"sv}},
66 * {"dataType"sv, VariantValue{"boolean"sv}},
67 * {"sendEvents"sv, VariantValue{"yes"sv}}}};
68 * Binding b{.fName = "stateVariable"sv,
69 * .fMembers = {{"sendEvents"sv, Binding{.fKind = Binding::Kind::eAttribute}}, {"name"sv}, {"dataType"sv}}};
70 * String xml = Variant::XML::Writer{{.fBinding = b}}.WriteAsString (v);
71 * VariantValue back = Variant::XML::Reader{b}.Read (xml); // == v
72 *
73 * // With no fName, the document element is the value's one member: its name moves from the Binding into the value.
74 * // So the same XML again, from v wrapped in a member of that name - whose Binding is b:
75 * VariantValue w{Mapping<String, VariantValue>{{"stateVariable"sv, v}}};
76 * Binding d{.fMembers = {{"stateVariable"sv, b}}};
77 * String xml2 = Variant::XML::Writer{{.fBinding = d}}.WriteAsString (w); // == xml
78 * VariantValue back2 = Variant::XML::Reader{d}.Read (xml2); // == w
79 * VariantValue plain = Variant::XML::Reader{}.Read (xml); // == w too: read with no Binding at all
80 * \endcode
81 * \code
82 * <stateVariable sendEvents="yes">
83 * <name>Status</name>
84 * <dataType>boolean</dataType>
85 * </stateVariable>
86 * \endcode
87 */
88 struct Binding {
89 /**
90 * \brief One member of an object: its key, and how that member differs from the default shape (nothing, by default -
91 * naming it is enough to give it a place in the order)
92 */
93 struct Member {
94 String fKey;
95 shared_ptr<Binding> fBinding;
96
97 Member (const String& key); ///< \brief the member's own key, and the default shape
98 Member (const String& key, const Binding& binding); ///< \brief the member's own key, and how it differs
99
100 bool operator== (const Member& rhs) const;
101
102 /**
103 * @see Characters::ToString ();
104 */
105 nonvirtual String ToString () const;
106 };
107
108 /**
109 * Whether the member this Binding goes with is written as a child element (the default), as an attribute of the
110 * element it is a member of, as that element's text, or as a presence marker. The document element is always an element.
111 *
112 * A presence marker (ePresenceMarker) is a Boolean member written as an element's presence: an empty element for true,
113 * none for false - as UPnP's `<retval/>`, which says an argument is the return value by being there. So a presence
114 * marker absent reads back as false.
115 */
116 enum class Kind {
117 eElement,
118 eAttribute,
119 eText,
120 ePresenceMarker
121 };
122 Kind fKind{Kind::eElement};
123
124 /**
125 * The element's (or attribute's) name, where that is not the member's own key - and its namespace. On the top level Binding,
126 * the document element's.
127 */
128 optional<NameWithNamespace> fName;
129
130 /**
131 * The type of the value this Binding goes with - nullopt (the default) leaving it to the value.
132 *
133 * **Declaring it changes what the Writer and Reader do, and a great deal.** With none, a Binding says only how a value
134 * is written: the Writer takes any value, and the Reader any document, guessing its shape. Declared, it also says which
135 * values there may be - as an XSD does - and the Writer and Reader throw BadFormatException on any other:
136 *
137 * | | fType nullopt - the default | fType declared |
138 * |-------------------------------|-------------------------------------|------------------------------------|
139 * | a leaf, read back | a String: 3 as "3" | that type: 3 as 3 |
140 * | text not of its type, read | - (every leaf reads as a String) | throws |
141 * | an empty element, read back | "" (but [] where fItems is set) | {} for eMap, [] for eArray |
142 * | a null member, written | as an empty element: read back "" | as none: read back absent |
143 * | another type's value, written | as it is (a leaf as its As<String>) | throws |
144 * | a member it does not name | written after those it names; read | throws (unless fUndeclaredMembers) |
145 * | a child named <Array>, read | an item, unless fMembers names it | an item only where fType is eArray |
146 *
147 * A null member is written as none as ObjectVariantMapper omits a null field. Per Binding, so per value: a member of a
148 * declared object whose own Binding declares no type is still written and read the first way. A Binding declaring the
149 * type of every value in it, and leaving nothing else to the default shape, is complete (@see IsComplete).
150 *
151 * @todo An eMap OPEN to members it does not declare (XSD's xs:any, JSON Schema's additionalProperties), if a need
152 * appears. Extra members left to the default shape could never be complete - their types lost, their names free
153 * to collide. The likely need is a dictionary (a Mapping<String, T> field, which a Binding generated from an
154 * ObjectVariantMapper will meet - https://github.com/SophistSolutions/Stroika/issues/1230): every extra member of
155 * ONE declared Binding. That can be complete only with its keys written as data, not as element names (a key
156 * need not be an XML name) - entries, as <e key="k">v</e>.
157 */
158 optional<VariantValue::Type> fType;
159
160 /**
161 * This object's members, in the order to write them. A member of the value that is not named here is written after
162 * those that are, in the order the value gives them - where fUndeclaredMembers is eKeep.
163 */
165
166 /**
167 * \brief What an object does with a member fMembers does not declare - an element or attribute in a document, or a
168 * value's member - as XSD's xs:any and xs:anyAttribute say (processContents)
169 */
170 enum class UndeclaredMembers {
171 /**
172 * reading or writing one throws BadFormatException: it is not of the Binding's type
173 */
174 eReject,
175 /**
176 * reading skips it, unread (so whatever it holds), as XSD's processContents="skip" - a vendor's own elements, say;
177 * writing one throws, as it would not read back
178 */
179 eSkip,
180 /**
181 * read and written in the default shape: a member by its name, and untyped - so a Binding keeping them is not complete
182 */
183 eKeep,
184 };
185
186 /**
187 * nullopt (the default) is eReject where fType is declared, and eKeep where not. eSkip is how a complete Binding reads
188 * someone else's documents - UPnP's descriptions, which devices extend with their own elements - and still round-trips
189 * every value of its type.
190 *
191 * An array (fType eArray) has no members, but eSkip skips what is not its item there too - an element or attribute
192 * among a list's items; anything else refuses it, as for a value of any other type.
193 */
194 optional<UndeclaredMembers> fUndeclaredMembers;
195
196 /**
197 * A Binding for each item of this array - and, reading, what says this member IS an array, however few elements the
198 * document happens to have (@see Variant::XML::Writer on that ambiguity).
199 *
200 * Where that Binding has an fName, it names the item element, and the member's own element wraps them - so one
201 * document can wrap two arrays differently, as UPnP's `<actionList><action>` and `<allowedValueList><allowedValue>`:
202 * \code
203 * // {"allowedValueList": ["yes", "no"]}
204 * Binding{.fMembers = {{"allowedValueList"sv,
205 * Binding{.fItems = make_shared<Binding> (Binding{.fName = "allowedValue"sv})}}}};
206 * \endcode
207 * \code
208 * <allowedValueList>
209 * <allowedValue>yes</allowedValue>
210 * <allowedValue>no</allowedValue>
211 * </allowedValueList>
212 * \endcode
213 * Where it has none, nothing wraps them: the member's own element is repeated, once per item.
214 *
215 * An array with no fItems has fDefaultArrayItems'.
216 */
217 shared_ptr<Binding> fItems;
218
219 /**
220 * \brief The Binding each item of an array is written with, where the array's own Binding has no fItems: its fName is
221 * the element each item is written as - and with none (kRepeatedArrayItems), the member's own element is
222 * repeated, once per item
223 *
224 * It holds for the arrays at and inside this element, until an element inside gives its own; nullptr takes the one the
225 * element this is inside has. The document starts with kDefaultArrayItems.
226 *
227 * \par Example Usage
228 * \code
229 * // v is {"serviceList": {"service": ["a", "b"]}}
230 *
231 * // nothing set - kDefaultArrayItems: an <Array> per item, wrapped in <service>
232 * Variant::XML::Writer{}.WriteAsString (v);
233 * // <serviceList>
234 * // <service>
235 * // <Array>a</Array>
236 * // <Array>b</Array>
237 * // </service>
238 * // </serviceList>
239 *
240 * // an item name: an <item> per item, wrapped in <service>
241 * Variant::XML::Writer{{.fBinding = Binding{.fDefaultArrayItems = make_shared<Binding> (Binding{.fName = "item"sv})}}}
242 * .WriteAsString (v);
243 * // <serviceList>
244 * // <service>
245 * // <item>a</item>
246 * // <item>b</item>
247 * // </service>
248 * // </serviceList>
249 *
250 * // no item name: <service> itself, repeated
251 * Variant::XML::Writer{{.fBinding = Binding{.fDefaultArrayItems = make_shared<Binding> (Binding::kRepeatedArrayItems)}}}
252 * .WriteAsString (v);
253 * // <serviceList>
254 * // <service>a</service>
255 * // <service>b</service>
256 * // </serviceList>
257 * \endcode
258 * Each reads back as it was written, given the same Binding - except an array of one item in the third, which reads back
259 * as that one item (a single `<service>a</service>` looks like any other member), unless the member's own Binding has
260 * fItems, saying it is an array:
261 *
262 * | service | nothing set | an item name | no item name |
263 * |--------------|--------------|--------------|--------------|
264 * | ["a", "b"] | ["a", "b"] | ["a", "b"] | ["a", "b"] |
265 * | ["a"] | ["a"] | ["a"] | "a" |
266 * | [] | "" | "" | absent |
267 *
268 * Reading, an element with the item name is an item - so is a member written with that name (a key "Array", in the
269 * default shape), unless the Binding of the object it is in names it in fMembers.
270 */
271 shared_ptr<Binding> fDefaultArrayItems;
272
273 /**
274 * \brief The Binding each item of an array is written with where neither fItems nor fDefaultArrayItems gives one: an
275 * `<Array>` element, wrapped in the member's own - so an array of one reads back as an array, though nothing says
276 * it is one
277 */
279
280 /**
281 * \brief An item Binding with no name, so no element of the items' own: the member's own element is repeated, once per
282 * item - as a schema of someone else's usually has an array. As fDefaultArrayItems, or a single array's fItems.
283 */
285
286 /**
287 * \brief A prefix for each of these namespaces, declared on this element (xmlns:p=) and used for every element and
288 * attribute at and inside it that is in that namespace: SOAP's s:Envelope, s:encodingStyle
289 *
290 * A namespace with no prefix is written as a default namespace (xmlns=) on the element that has it, which an attribute
291 * cannot be: an attribute in a namespace requires a prefix, declared here on its element or one it is inside. Writing
292 * only - reading takes namespaces from the document.
293 *
294 * \par Example Usage
295 * \code
296 * // SOAP's envelope: {"encodingStyle": "http://schemas.xmlsoap.org/soap/encoding/", "Body": {"GetStatus": ""}}
297 * const URI kSOAP{"http://schemas.xmlsoap.org/soap/envelope/"sv};
298 * Binding b{.fName = NameWithNamespace{kSOAP, "Envelope"sv},
299 * .fMembers = {{"encodingStyle"sv, Binding{.fKind = Binding::Kind::eAttribute,
300 * .fName = NameWithNamespace{kSOAP, "encodingStyle"sv}}},
301 * {"Body"sv, Binding{.fName = NameWithNamespace{kSOAP, "Body"sv}}}},
302 * .fNamespacePrefixes = {{kSOAP, "s"sv}}};
303 * \endcode
304 * \code
305 * <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="...">
306 * <s:Body>
307 * <GetStatus></GetStatus>
308 * </s:Body>
309 * </s:Envelope>
310 * \endcode
311 */
313
314 /**
315 * \brief The Binding for the member of this object with that key - nullptr where it has none, so the default shape
316 */
317 nonvirtual const Binding* MemberBinding (const String& key) const;
318
319 /**
320 * \brief The name that member is written with: its Binding's fName, else its own key
321 */
322 nonvirtual NameWithNamespace NameFor (const String& key) const;
323
324 /**
325 * \brief That member's kind: its Binding's, else an element
326 */
327 nonvirtual Kind KindFor (const String& key) const;
328
329 /**
330 * \brief Reading: the key of the member of that kind (an element - or presence marker - or an attribute) written with that local
331 * name - the name itself, where no such member is named
332 *
333 * By kind, as an element and an attribute may have the same name. A presence marker is an element.
334 */
335 nonvirtual String KeyForName (Kind kind, const String& name) const;
336
337 /**
338 * \brief Reading: the key of the member that is this element's text, if one is
339 */
340 nonvirtual optional<String> TextMemberKey () const;
341
342 /**
343 * \brief fUndeclaredMembers, or its default: eReject where fType is declared, eKeep where not
344 */
345 nonvirtual UndeclaredMembers GetUndeclaredMembers () const;
346
347 /**
348 * \brief Whether, given this Binding as the top level Binding (the one a Reader or Writer is given), the Writer and Reader
349 * round-trip every VariantValue faithfully, or throw - never anything in between
350 *
351 * **Given a complete Binding, writing a VariantValue and reading it back gives a value equal to it - or throws
352 * BadFormatException, the value not being one the Binding describes.** Equal values, not the same document: @see
353 * Reader. The Writer throws on a value of another type, with a member the Binding does not declare, or holding a
354 * character XML 1.0 cannot; the Reader, on a document not of the Binding's type (an element or
355 * attribute it does not declare, a member repeated that it declares as one value, text that is not of its type).
356 * Nothing is guessed, and nothing silently lost - but for what XML writes as nothing (below).
357 *
358 * Complete means it leaves nothing to the default shape, or to the value:
359 * - it names the document element (fName), and each Binding in it declares fType
360 * - each member of an object (fMembers) has a Binding, and is written with an XML name no other element member - or,
361 * for an attribute, no other attribute - of that object has (by local name, as the Reader matches them)
362 * - each array has fItems; an array whose items are its member's element repeated is an object's member (not an
363 * array's item, nor the document element)
364 * - attributes and text are leaves, a text member is beside attributes only, and an attribute in a namespace has a
365 * prefix (fNamespacePrefixes) in scope
366 *
367 * The one exception, by design: XML has no way to write a null member, an empty array of repeated elements, or a text
368 * member (Kind::eText) whose text is empty - an empty string or BLOB - but as nothing, so each reads back absent; and a
369 * presence marker (Kind::ePresenceMarker) null or absent reads back false, as a false one is written as nothing too.
370 * (As ObjectVariantMapper omits a null field anyway, and reads an absent one as its default.)
371 *
372 * A complete Binding may skip what it does not declare (fUndeclaredMembers eSkip): reading someone else's document then
373 * skips a vendor's own elements, while the round trip of the Binding's own values stays exact.
374 *
375 * A Binding generated from a type's declaration (an ObjectVariantMapper's -
376 * https://github.com/SophistSolutions/Stroika/issues/1230) can be complete by construction; one written by hand may
377 * instead be short, leaving the rest to the default shape. ToString () says which - and, where it is not complete, why.
378 */
379 nonvirtual bool IsComplete () const;
380
381 /**
382 * \brief Equal where every field is - fItems and fDefaultArrayItems by the Bindings they point to, not the pointers
383 */
384 bool operator== (const Binding& rhs) const;
385
386 /**
387 * @see Characters::ToString ();
388 */
389 nonvirtual String ToString () const;
390
391 private:
392 // why this Binding, as the top level Binding, is not complete - the first thing found; nullopt where it is
393 nonvirtual optional<String> WhyIncomplete_ () const;
394 // with whether it is complete only where top: a Binding inside another is not the top level one
395 nonvirtual String ToString_ (bool top) const;
396 };
397
398}
399
400#endif /*_Stroika_Foundation_DataExchange_Variant_XML_Binding_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
A generalization of a vector: a container whose elements are keyed by the natural numbers.
One member of an object: its key, and how that member differs from the default shape (nothing,...
Definition Binding.h:93
How a VariantValue is to be written as XML, and read back, where the default shape (.
Definition Binding.h:88
bool operator==(const Binding &rhs) const
Equal where every field is - fItems and fDefaultArrayItems by the Bindings they point to,...
Definition Binding.cpp:272
nonvirtual bool IsComplete() const
Whether, given this Binding as the top level Binding (the one a Reader or Writer is given),...
Definition Binding.cpp:251
nonvirtual String KeyForName(Kind kind, const String &name) const
Reading: the key of the member of that kind (an element - or presence marker - or an attribute) writt...
Definition Binding.cpp:226
static const Binding kDefaultArrayItems
The Binding each item of an array is written with where neither fItems nor fDefaultArrayItems gives o...
Definition Binding.h:278
nonvirtual optional< String > TextMemberKey() const
Reading: the key of the member that is this element's text, if one is.
Definition Binding.cpp:236
nonvirtual Kind KindFor(const String &key) const
That member's kind: its Binding's, else an element.
Definition Binding.cpp:220
nonvirtual NameWithNamespace NameFor(const String &key) const
The name that member is written with: its Binding's fName, else its own key.
Definition Binding.cpp:212
nonvirtual UndeclaredMembers GetUndeclaredMembers() const
fUndeclaredMembers, or its default: eReject where fType is declared, eKeep where not
Definition Binding.cpp:246
UndeclaredMembers
What an object does with a member fMembers does not declare - an element or attribute in a document,...
Definition Binding.h:170
Mapping< URI, String > fNamespacePrefixes
A prefix for each of these namespaces, declared on this element (xmlns:p=) and used for every element...
Definition Binding.h:312
static const Binding kRepeatedArrayItems
An item Binding with no name, so no element of the items' own: the member's own element is repeated,...
Definition Binding.h:284
nonvirtual const Binding * MemberBinding(const String &key) const
The Binding for the member of this object with that key - nullptr where it has none,...
Definition Binding.cpp:202
shared_ptr< Binding > fDefaultArrayItems
The Binding each item of an array is written with, where the array's own Binding has no fItems: its f...
Definition Binding.h:271
An element's or attribute's name (local-name, not including a prefix), plus the namespace that name i...
Definition Namespace.h:63