bpmn++
A BPMN parser library, written in C++
Loading...
Searching...
No Matches
XMLObject.h
Go to the documentation of this file.
1// schematic++ v0.8.0
2#ifndef XMLObject_H
3#define XMLObject_H
4#include <memory>
5#include <sstream>
6#include <string>
7#include <string_view>
8#include <unordered_map>
9#include <vector>
10#include <optional>
11#include <functional>
12#include <type_traits>
13
14#include <xercesc/dom/DOM.hpp>
15
16/**
17 * @brief The `XML` namespace contains classes representing XML-nodes defined in given XML-schema(s).
18 */
19namespace XML {
20
21class XMLObject;
22
23typedef std::string ClassName;
24typedef std::string ElementName;
25typedef std::string TextContent;
26typedef std::string Namespace;
27typedef std::string AttributeName;
28
29/**
30 * @brief A struct representing the value of an XML-node attribute.
31 *
32 * The Value struct stores a value and provides implicit conversion and assignment operators
33 * to facilitate easy conversion between different types and convenient assignment
34 * of values.
35 *
36 * Example usage:
37 * ```
38 * Value value;
39 * value = "a_string"; // Assignment using a std::string.
40 * value = true; // Assignment using a bool.
41 * value = 42; // Assignment using an int.
42 * value = 3.14; // Assignment using a double.
43 *
44 * std::string stringValue = value; // Implicit conversion to std::string.
45 * bool booleanValue = value; // Implicit conversion to bool.
46 * int integerValue = value; // Implicit conversion to int.
47 * double realValue = value; // Implicit conversion to double.
48 * ```
49 */
50struct Value {
51 std::string value;
52 operator std::string_view() const { return value; };
53 operator std::string() const { return value; };
54 operator bool() const { return (value == True); };
55 operator int() const { try { return std::stoi(value); } catch(...) { throw std::runtime_error("Cannot convert '" + value + "' to int"); } };
56 operator double() const { try { return std::stod(value); } catch(...) { throw std::runtime_error("Cannot convert '" + value + "' to double"); } };
57 Value& operator=(const std::string& s) { value = s; return *this; };
58 Value& operator=(bool b) { value = (b ? True : False); return *this; };
59 Value& operator=(int i) { value = std::to_string(i); return *this; };
60 Value& operator=(double d) { value = std::to_string(d); return *this; };
61 Value(const std::string& s) : value(s) {};
62 Value(bool b) : value(b ? True : False) {};
63 Value(int i) : value(std::to_string(i)) {};
64 Value(double d) : value(std::to_string(d)) {};
65 inline static std::string True = "true";
66 inline static std::string False = "false";
67};
68
69/**
70 * @brief A struct representing an attribute of an XML-node.
71 *
72 * The `Attribute` struct stores information about the namespace, prefix, name, and
73 * value of the attribute.
74 */
81
82typedef std::vector<Attribute> Attributes;
83typedef std::vector<std::unique_ptr<XMLObject>> Children;
84
85/// @brief Template function used to store in factory
86template<typename T> XMLObject* createInstance(const Namespace& xmlns, const ClassName& className, const xercesc::DOMElement* element) { return new T(xmlns, className, element, T::defaults); }
87
88/// @brief Factory used to create instance depending on element name
89typedef std::unordered_map<ElementName, XMLObject* (*)(const Namespace& xmlns, const ClassName& className, const xercesc::DOMElement* element)> Factory;
90
91/**
92 * @brief Yields `const T` when the deduced `Self` is a const-qualified object, otherwise `T`.
93 *
94 * Used together with C++23 explicit object parameters ("deducing this") so that a single
95 * accessor definition returns const-qualified references from a const object and mutable
96 * references from a mutable object. This is needed because child access goes through
97 * `std::unique_ptr<XMLObject>`, which does not propagate the object's const-ness to the
98 * pointee on its own.
99 */
100template<typename Self, typename T>
101using like_const_t = std::conditional_t<std::is_const_v<std::remove_reference_t<Self>>, const T, T>;
102
103
104/**
105 * @brief A class representing a node in an XML-tree.
106 *
107 * The XMLObject class allows to read and store an XML-tree. The root element can be created using
108 * - @ref XMLObject::createFromStream(std::istream& xmlStream)
109 * - @ref XMLObject::createFromString(const std::string& xmlString)
110 * - @ref XMLObject::createFromFile(const std::string& filename)
111 *
112 * Each object has the following members:
113 * - @ref xmlns : refers to the XML namespace
114 * - @ref className : refers to the class it belong to
115 * - @ref elementName : refers to the name used in the XML
116 * - @ref prefix : refers to the namespace prefix in the XML
117 * - @ref textContent : textual content of XML element without children
118 * - @ref attributes : a list of attributes containing the namespace, prefix, attribute name,
119 * and attribute value
120 * - @ref children : a list of child elements
121 *
122 * Derived classes with dedicated members for attributes and children are automatically generated by
123 * schematic++ according to respective XML schema definition(s).
124 *
125 * Each XMLObject can be converted to a string using @ref stringify() and printed to an output stream
126 * using @ref operator<<(std::ostream& os, const XMLObject* obj) and
127 * @ref operator<<(std::ostream& os, const XMLObject& obj) .
128 */
130
131public:
132 /**
133 * @brief Create an XMLObject from the input stream.
134 *
135 * @param xmlStream The input stream containing the XML data.
136 * @return A pointer to the created XMLObject.
137 * @throws std::runtime_error if parsing the XML fails.
138 */
139 static XMLObject* createFromStream(std::istream& xmlStream);
140
141 /**
142 * @brief Create an XMLObject from a string representation of XML.
143 *
144 * @param xmlString The string containing the XML data.
145 * @return A pointer to the created XMLObject.
146 * @throws std::runtime_error if parsing the XML fails.
147 */
148 static XMLObject* createFromString(const std::string& xmlString);
149
150 /**
151 * @brief Create an XMLObject from an XML file.
152 *
153 * @param filename The path to the XML file.
154 * @return A pointer to the created XMLObject.
155 * @throws std::runtime_error if loading the file or parsing the XML fails.
156 */
157 static XMLObject* createFromFile(const std::string& filename);
158
159 virtual ~XMLObject() {};
160
161protected:
162 static XMLObject* createObject(const xercesc::DOMElement* element);
163
164template<typename T> friend XMLObject* createInstance(const Namespace& xmlns, const ClassName& className, const xercesc::DOMElement* element);
165
166protected:
167 XMLObject(const Namespace& xmlns, const ClassName& className, const xercesc::DOMElement* element, const Attributes& defaultAttributes);
168
169 inline static Factory factory;
170public:
171 /// @brief Returns a pointer of type T of the object, const-qualified to match the receiver.
172 template<typename T, typename Self> inline auto* is(this Self&& self) {
173 return dynamic_cast<like_const_t<Self, T>*>(&self);
174 }
175
176 /**
177 * @brief Attempt to cast the current instance to the specified type T.
178 * @param self The object the call is made on; its const-ness determines that of the result.
179 * @return A pointer to the casted object, const-qualified to match the receiver.
180 * @throws std::runtime_error if the cast fails.
181 */
182 template<typename T, typename Self> inline auto* get(this Self&& self) {
183 auto* ptr = self.template is<T>();
184 if ( ptr == nullptr ) {
185 throw std::runtime_error("Failed to cast element '" + self.XMLObject::elementName + "' from class '" + self.XMLObject::className + "' to type '" + typeid(T).name() + "'");
186 }
187 return ptr;
188 }
189
190private:
191 // T already carries the desired const-ness (baked in by find() below), so this can stay a
192 // single const member: child access via unique_ptr yields a mutable XMLObject& regardless.
193 template<typename T>
194 void findRecursive(std::vector<std::reference_wrapper<T> >& result, const Children& descendants) const
195 {
196 for (auto& descendant : descendants) {
197 if (auto* ptr = descendant->template is<T>()) {
198 result.push_back(*ptr);
199 }
200 findRecursive(result, descendant->children );
201 }
202 }
203
204public:
205 /**
206 * @brief Find all descendants of type T.
207 *
208 * @return A vector of references to descendants of type T,
209 * const-qualified to match the receiver.
210 */
211 template<typename T, typename Self>
212 auto find(this Self&& self)
213 {
214 std::vector<std::reference_wrapper<like_const_t<Self, T> > > result;
215 self.findRecursive(result, self.XMLObject::children);
216 return result;
217 }
218
223
224 TextContent textContent; ///< Textual content of XML element without children
225 Children children; ///< Child nodes of the XML element
226 Attributes attributes; /// Attributes of the XML element
227 inline static const Attributes defaults = {};
228
229 /**
230 * @brief Convert the XMLObject and its children to a string representation.
231 *
232 * @return The string representation of the XMLObject.
233 */
234 std::string stringify() const;
235
236 /**
237 * @brief Creates formatted string representing the XMLObject including its children.
238 *
239 * @return A formatted string representing the XMLObject.
240 */
241 std::string format(std::string indentation = "\t", unsigned int depth = 0) const;
242
243 /**
244 * @brief Get a required child of type T.
245 *
246 * @param self The object the call is made on; its const-ness determines that of the result.
247 * @return A reference to the required child, const-qualified to match the receiver.
248 * @throws std::runtime_error if the required child is not found.
249 */
250 template<typename T, typename Self> auto& getRequiredChild(this Self&& self) {
251 for ( auto& child : self.XMLObject::children ) {
252 if ( auto* ptr = child->template is<like_const_t<Self, T> >() ) {
253 return *ptr;
254 }
255 }
256 throw std::runtime_error("Failed to find required child of type '" + std::string(typeid(T).name()) + "' in element '" + self.XMLObject::elementName + "'");
257 }
258
259 /**
260 * @brief Get an optional child of type T.
261 *
262 * @param self The object the call is made on; its const-ness determines that of the result.
263 * @return An optional reference to the optional child (const-qualified to match the
264 * receiver) if found, or `std::nullopt` if the optional child is not found.
265 */
266 template<typename T, typename Self> auto getOptionalChild(this Self&& self) {
267 using E = like_const_t<Self, T>;
268 for ( auto& child : self.XMLObject::children ) {
269 if ( auto* ptr = child->template is<E>() ) {
270 return std::optional< std::reference_wrapper<E> >(*ptr);
271 }
272 }
273 return std::optional< std::reference_wrapper<E> >(std::nullopt);
274 }
275
276 /**
277 * @brief Get all children of type T.
278 *
279 * @param self The object the call is made on; its const-ness determines that of the result.
280 * @return A vector of references to the children of type T,
281 * const-qualified to match the receiver.
282 */
283 template<typename T, typename Self> auto getChildren(this Self&& self) {
284 using E = like_const_t<Self, T>;
285 std::vector< std::reference_wrapper<E> > result;
286 for ( auto& child : self.XMLObject::children ) {
287 if ( auto* ptr = child->template is<E>() ) {
288 result.push_back(*ptr);
289 }
290 }
291 return result;
292 }
293
294 /**
295 * @brief Get a required child with the specified element name.
296 *
297 * @param self The object the call is made on; its const-ness determines that of the result.
298 * @param name The name of the child element without namespace prefix.
299 * @return A reference to the required child, const-qualified to match the receiver.
300 * @throws std::runtime_error if the required child is not found.
301 */
302 template<typename Self> auto& getRequiredChildByName(this Self&& self, const ElementName& name) {
303 for ( auto& child : self.XMLObject::children ) {
304 if ( child->elementName == name ) {
305 return static_cast<like_const_t<Self, XMLObject>&>(*child);
306 }
307 }
308 throw std::runtime_error("Failed to get required child '" + name + "' of element '" + self.XMLObject::elementName + "'");
309 }
310
311 /**
312 * @brief Get the optional child with the specified element name.
313 *
314 * @param self The object the call is made on; its const-ness determines that of the result.
315 * @param name The name of the child element without namespace prefix.
316 * @return An optional reference to the optional child (const-qualified to match the
317 * receiver) if found, or `std::nullopt` if the optional child is not found.
318 */
319 template<typename Self> auto getOptionalChildByName(this Self&& self, const ElementName& name) {
321 for ( auto& child : self.XMLObject::children ) {
322 if ( child->elementName == name ) {
323 return std::optional< std::reference_wrapper<O> >(static_cast<O&>(*child));
324 }
325 }
326 return std::optional< std::reference_wrapper<O> >(std::nullopt);
327 }
328
329 /**
330 * @brief Get all children with the specified element name.
331 *
332 * @param self The object the call is made on; its const-ness determines that of the result.
333 * @param name The name of the child elements without namespace prefix.
334 * @return A vector of references to the children with the specified element name,
335 * const-qualified to match the receiver.
336 */
337 template<typename Self> auto getChildrenByName(this Self&& self, const ElementName& name) {
339 std::vector< std::reference_wrapper<O> > result;
340 for ( auto& child : self.XMLObject::children ) {
341 if ( child->elementName == name ) {
342 result.push_back(static_cast<O&>(*child));
343 }
344 }
345 return result;
346 }
347
348 /**
349 * @brief Get a required attribute with the specified attribute name.
350 *
351 * @param self The object the call is made on; its const-ness determines that of the result.
352 * @param name The name of the attribute without namespace prefix.
353 * @return A reference to the required attribute, const-qualified to match the receiver.
354 * @throws std::runtime_error if the required attribute is not found.
355 */
356 template<typename Self> auto& getRequiredAttributeByName(this Self&& self, const AttributeName& name) {
357 for ( auto& attribute : self.XMLObject::attributes ) {
358 if ( attribute.name == name ) {
359 return attribute;
360 }
361 }
362 throw std::runtime_error("Failed to get required attribute '" + name + "' of element '" + self.XMLObject::elementName + "'");
363 }
364
365 /**
366 * @brief Get an optional attribute with the specified attribute name.
367 *
368 * @param self The object the call is made on; its const-ness determines that of the result.
369 * @param name The name of the attribute without namespace prefix.
370 * @return An optional reference to the optional attribute (const-qualified to match the
371 * receiver) if found, or `std::nullopt` if the optional attribute is not found.
372 */
373 template<typename Self> auto getOptionalAttributeByName(this Self&& self, const AttributeName& name) {
375 for ( auto& attribute : self.XMLObject::attributes ) {
376 if ( attribute.name == name ) {
377 return std::optional< std::reference_wrapper<A> >(attribute);
378 }
379 }
380 return std::optional< std::reference_wrapper<A> >(std::nullopt);
381 }
382
383
384};
385
386/// @brief Allows printing of stringified XML object
387std::ostream& operator<<(std::ostream& os, const XMLObject* obj);
388/// @brief Allows printing of stringified XML object
389std::ostream& operator<<(std::ostream& os, const XMLObject& obj);
390
391} // end namespace XML
392
393#endif // XML_H
A class representing a node in an XML-tree.
Definition XMLObject.h:129
auto getOptionalChild(this Self &&self)
Get an optional child of type T.
Definition XMLObject.h:266
auto * get(this Self &&self)
Attempt to cast the current instance to the specified type T.
Definition XMLObject.h:182
Attributes attributes
Definition XMLObject.h:226
static XMLObject * createFromFile(const std::string &filename)
Create an XMLObject from an XML file.
Definition XMLObject.cpp:81
static XMLObject * createFromString(const std::string &xmlString)
Create an XMLObject from a string representation of XML.
Definition XMLObject.cpp:75
static XMLObject * createObject(const xercesc::DOMElement *element)
auto & getRequiredChild(this Self &&self)
Get a required child of type T.
Definition XMLObject.h:250
ElementName elementName
Definition XMLObject.h:222
const ClassName className
Definition XMLObject.h:220
auto & getRequiredChildByName(this Self &&self, const ElementName &name)
Get a required child with the specified element name.
Definition XMLObject.h:302
auto getChildren(this Self &&self)
Get all children of type T.
Definition XMLObject.h:283
auto getOptionalAttributeByName(this Self &&self, const AttributeName &name)
Get an optional attribute with the specified attribute name.
Definition XMLObject.h:373
std::string format(std::string indentation="\t", unsigned int depth=0) const
Creates formatted string representing the XMLObject including its children.
Children children
Child nodes of the XML element.
Definition XMLObject.h:225
Namespace prefix
Definition XMLObject.h:221
auto getChildrenByName(this Self &&self, const ElementName &name)
Get all children with the specified element name.
Definition XMLObject.h:337
auto & getRequiredAttributeByName(this Self &&self, const AttributeName &name)
Get a required attribute with the specified attribute name.
Definition XMLObject.h:356
TextContent textContent
Textual content of XML element without children.
Definition XMLObject.h:224
std::string stringify() const
Convert the XMLObject and its children to a string representation.
XMLObject(const Namespace &xmlns, const ClassName &className, const xercesc::DOMElement *element, const Attributes &defaultAttributes)
friend XMLObject * createInstance(const Namespace &xmlns, const ClassName &className, const xercesc::DOMElement *element)
Template function used to store in factory.
Definition XMLObject.h:86
auto find(this Self &&self)
Find all descendants of type T.
Definition XMLObject.h:212
static XMLObject * createFromStream(std::istream &xmlStream)
Create an XMLObject from the input stream.
Definition XMLObject.cpp:47
static const Attributes defaults
Attributes of the XML element.
Definition XMLObject.h:227
virtual ~XMLObject()
Definition XMLObject.h:159
auto getOptionalChildByName(this Self &&self, const ElementName &name)
Get the optional child with the specified element name.
Definition XMLObject.h:319
static Factory factory
Definition XMLObject.h:169
Namespace xmlns
Definition XMLObject.h:219
auto * is(this Self &&self)
Returns a pointer of type T of the object, const-qualified to match the receiver.
Definition XMLObject.h:172
The XML namespace contains classes representing XML-nodes defined in given XML-schema(s).
Definition XMLObject.cpp:9
std::string ElementName
Definition XMLObject.h:24
std::string Namespace
Definition XMLObject.h:26
std::ostream & operator<<(std::ostream &os, const XMLObject *obj)
Allows printing of stringified XML object.
std::vector< Attribute > Attributes
Definition XMLObject.h:82
std::vector< std::unique_ptr< XMLObject > > Children
Definition XMLObject.h:83
std::string ClassName
Definition XMLObject.h:23
XMLObject * createInstance(const Namespace &xmlns, const ClassName &className, const xercesc::DOMElement *element)
Template function used to store in factory.
Definition XMLObject.h:86
std::unordered_map< ElementName, XMLObject *(*)(const Namespace &xmlns, const ClassName &className, const xercesc::DOMElement *element)> Factory
Factory used to create instance depending on element name.
Definition XMLObject.h:89
std::conditional_t< std::is_const_v< std::remove_reference_t< Self > >, const T, T > like_const_t
Yields const T when the deduced Self is a const-qualified object, otherwise T.
Definition XMLObject.h:101
std::string AttributeName
Definition XMLObject.h:27
std::string TextContent
Definition XMLObject.h:25
A struct representing an attribute of an XML-node.
Definition XMLObject.h:75
Namespace prefix
Definition XMLObject.h:77
AttributeName name
Definition XMLObject.h:78
Namespace xmlns
Definition XMLObject.h:76
A struct representing the value of an XML-node attribute.
Definition XMLObject.h:50
Value(const std::string &s)
Definition XMLObject.h:61
Value & operator=(bool b)
Definition XMLObject.h:58
Value(bool b)
Definition XMLObject.h:62
Value & operator=(double d)
Definition XMLObject.h:60
Value(int i)
Definition XMLObject.h:63
Value & operator=(const std::string &s)
Definition XMLObject.h:57
Value & operator=(int i)
Definition XMLObject.h:59
static std::string False
Definition XMLObject.h:66
static std::string True
Definition XMLObject.h:65
std::string value
Definition XMLObject.h:51
Value(double d)
Definition XMLObject.h:64