bpmn++
A BPMN parser library, written in C++
Loading...
Searching...
No Matches
XMLObject.cpp
Go to the documentation of this file.
1#include "XMLObject.h"
2#include <xercesc/parsers/XercesDOMParser.hpp>
3#include <xercesc/util/BinInputStream.hpp>
4#include <xercesc/sax/InputSource.hpp>
5#include <iostream>
6#include <algorithm>
7#include <filesystem>
8
9namespace XML {
10
11// Utility class for parsing directly from an std::istream.
12class IStreamInputSource : public xercesc::InputSource {
13protected:
14 std::istream &is;
15 xercesc::BinInputStream* makeStream() const {
16 return new IStreamBinInputStream(is);
17 }
18
19 class IStreamBinInputStream : public xercesc::BinInputStream {
20 std::istream &is;
21 public:
22 IStreamBinInputStream(std::istream &is) : xercesc::BinInputStream(), is(is) {};
23 XMLFilePos curPos(void) const { return (XMLFilePos)is.tellg(); };
24 XMLSize_t readBytes(XMLByte* const buf, const XMLSize_t max) {
25 is.read((char*)buf, (std::streamsize)max);
26 return (XMLSize_t)is.gcount();
27 };
28 const XMLCh* getContentType() const {
29 //TODO: return application/xml
30 return NULL;
31 };
32 };
33public:
34 IStreamInputSource(std::istream &is) : InputSource(), is(is) {};
35};
36
37std::string transcode(const XMLCh* xmlChStr) {
38 char* cStr = xercesc::XMLString::transcode(xmlChStr);
39 if (!cStr) {
40 throw std::runtime_error("Failed to transcode XML string");
41 }
42 std::string result(cStr);
43 xercesc::XMLString::release(&cStr);
44 return result;
45}
46
47XMLObject* XMLObject::createFromStream(std::istream& xmlStream) {
48 // std::cout << "Create XML object from input stream" << std::endl;
49 xercesc::XMLPlatformUtils::Initialize();
50 std::unique_ptr<xercesc::XercesDOMParser> parser = std::make_unique<xercesc::XercesDOMParser>();
51 parser->setDoNamespaces(true);
52 parser->parse(IStreamInputSource(xmlStream));
53
54 xercesc::DOMDocument* document = parser->getDocument();
55 if (!document) {
56 parser.reset(); // delete unique_ptr to parser before calling Terminate
57 xercesc::XMLPlatformUtils::Terminate();
58 throw std::runtime_error("Failed to parse XML stream");
59 }
60
61 xercesc::DOMElement* rootElement = document->getDocumentElement();
62 if (!rootElement) {
63 parser.reset(); // delete unique_ptr to parser before calling Terminate
64 xercesc::XMLPlatformUtils::Terminate();
65 throw std::runtime_error("Failed to get root element of XML stream");
66 }
67
68 std::string rootName = transcode(rootElement->getLocalName());
69 XMLObject* object = createObject(rootElement);
70 parser.reset(); // delete unique_ptr to parser before calling Terminate
71 xercesc::XMLPlatformUtils::Terminate();
72 return object;
73}
74
75XMLObject* XMLObject::createFromString(const std::string& xmlString) {
76 // std::cout << "Create XML object from string" << std::endl;
77 std::istringstream iss(xmlString);
78 return createFromStream(iss);
79}
80
81XMLObject* XMLObject::createFromFile(const std::string& filename) {
82 // std::cout << "Create XML object from file" << std::endl;
83 const std::filesystem::path filePath(filename);
84 if (!std::filesystem::exists(filePath)) {
85 throw std::runtime_error("XML file '" + std::filesystem::absolute(filePath).string() + "' not found");
86 }
87
88 xercesc::XMLPlatformUtils::Initialize();
89 std::unique_ptr<xercesc::XercesDOMParser> parser = std::make_unique<xercesc::XercesDOMParser>();
90 parser->setDoNamespaces(true);
91 XMLCh* xmlFilename = xercesc::XMLString::transcode(filename.c_str());
92 parser->parse(xmlFilename);
93 xercesc::XMLString::release(&xmlFilename); // Release memory after usage
94
95
96 xercesc::DOMDocument* document = parser->getDocument();
97 if (!document) {
98 parser.reset(); // delete unique_ptr to parser before calling Terminate
99 xercesc::XMLPlatformUtils::Terminate();
100 throw std::runtime_error("Failed to load and parse XML file: " + filename);
101 }
102
103 xercesc::DOMElement* rootElement = document->getDocumentElement();
104 if (!rootElement) {
105 parser.reset(); // delete unique_ptr to parser before calling Terminate
106 xercesc::XMLPlatformUtils::Terminate();
107 throw std::runtime_error("Failed to get root element of XML file: " + filename);
108 }
109
110 std::string rootName = transcode(rootElement->getLocalName());
111
112 XMLObject* object = createObject(rootElement);
113 parser.reset(); // delete unique_ptr to parser before calling Terminate
114 xercesc::XMLPlatformUtils::Terminate();
115 return object;
116}
117
118
119XMLObject* XMLObject::createObject(const xercesc::DOMElement* element) {
120 Namespace xmlns = transcode(element->getNamespaceURI());
121 ElementName elementName = transcode(element->getLocalName());
122 if ( auto it = factory.find(xmlns + ":" + elementName); it != factory.end() ) {
123 return it->second(xmlns, elementName, element);
124 }
125 // std::cout << "Unknown element '" << elementName << "' using 'XMLObject' instead" << std::endl;
126 return createInstance<XMLObject>(xmlns, "XMLObject", element);
127}
128
129XMLObject::XMLObject(const Namespace& xmlns, const ClassName& className, const xercesc::DOMElement* element, const Attributes& defaultAttributes) : xmlns(xmlns), className(className) {
130
131 prefix = transcode(element->getPrefix());
132 elementName = transcode(element->getLocalName());
133
134 // set attributes
135 xercesc::DOMNamedNodeMap* elementAttributes = element->getAttributes();
136 for (XMLSize_t i = 0; i < elementAttributes->getLength(); i++) {
137 xercesc::DOMNode* item = elementAttributes->item(i);
138
139 AttributeName attributeName = transcode(item->getLocalName());
140 // get namespace from atrribute or parent element
141 Namespace attributeXmlns = item->getNamespaceURI() ? transcode(item->getNamespaceURI()) : xmlns;
142 Namespace attributePrefix = item->getPrefix() ? transcode(item->getPrefix()) : "";
143 Value attributeValue(transcode(item->getNodeValue()));
144 attributes.push_back( { attributeXmlns, attributePrefix, attributeName, attributeValue } );
145 }
146
147 // add defaults for missing attributes
148 for ( auto& defaultAttribute : defaultAttributes ) {
149 if ( !getOptionalAttributeByName(defaultAttribute.name) ) {
150 attributes.push_back(defaultAttribute);
151 }
152 }
153
154 // set children
155 for (xercesc::DOMElement *childElement = element->getFirstElementChild(); childElement; childElement = childElement->getNextElementSibling()) {
156 children.push_back(std::unique_ptr<XMLObject>(createObject(childElement)));
157 }
158
159 if ( children.empty() ) {
160 textContent = transcode(element->getTextContent());
161 }
162}
163
164
166 for ( auto& child : children ) {
167 if ( child->elementName == name ) {
168 return *child;
169 }
170 }
171 throw std::runtime_error("Failed to get required child '" + name + "' of element '" + elementName + "'");
172}
173
174std::optional< std::reference_wrapper<XMLObject> > XMLObject::getOptionalChildByName(const ElementName& elementName) {
175 for ( auto& child : children ) {
176 if ( child->elementName == elementName ) {
177 return *child;
178 }
179 }
180 return std::nullopt;
181}
182
183std::vector< std::reference_wrapper<XMLObject> > XMLObject::getChildrenByName(const ElementName& name) {
184 std::vector< std::reference_wrapper<XMLObject> > result;
185 for ( auto& child : children ) {
186 if ( child->elementName == name ) {
187 result.push_back(*child);
188 }
189 }
190 return result;
191}
192
194 auto it = std::find_if(attributes.begin(), attributes.end(),
195 [name](Attribute& attribute) { return attribute.name == name; }
196 );
197 if (it != attributes.end()) {
198 return *it;
199 }
200 throw std::runtime_error("Failed to get required attribute '" + name + "' of element '" + elementName + "'");
201}
202
203std::optional< std::reference_wrapper<Attribute> > XMLObject::getOptionalAttributeByName(const AttributeName& name) {
204 auto it = std::find_if(attributes.begin(), attributes.end(),
205 [name](Attribute& attribute) { return attribute.name == name; }
206 );
207 if (it != attributes.end()) {
208 return *it;
209 }
210 return std::nullopt;
211}
212
213std::string XMLObject::stringify() const {
214 std::string xmlString = std::string("<") + (!prefix.empty() ? prefix + ":" : "") + elementName;
215 for ( auto& attribute : attributes ) {
216 xmlString += std::string(" ") + (!attribute.prefix.empty() ? attribute.prefix + ":" : "") + attribute.name + "=\"" + attribute.value.value +"\"";
217 }
218 xmlString += ">";
219
220 for ( auto& child : children ) {
221 xmlString += child->stringify();
222 }
223 xmlString += textContent;
224 xmlString += std::string("</") + (!prefix.empty() ? prefix + ":" : "") + elementName + ">";
225 return xmlString;
226}
227
228std::string XMLObject::format(std::string indentation, unsigned int depth) const {
229 // lambda to repeat indentation n times
230 auto indent = [&indentation](unsigned int n) -> std::string {
231 std::string result;
232 for (unsigned int i = 0; i < n; ++i) {
233 result += indentation;
234 }
235 return result;
236 };
237
238 std::string xmlString = indent(depth) + std::string("<") + (!prefix.empty() ? prefix + ":" : "") + elementName;
239 for ( auto& attribute : attributes ) {
240 xmlString += std::string(" ") + (!attribute.prefix.empty() ? attribute.prefix + ":" : "") + attribute.name + "=\"" + attribute.value.value +"\"";
241 }
242 xmlString += ">\n";
243
244 for ( auto& child : children ) {
245 xmlString += child->format(indentation, depth+1);
246 }
247 xmlString += textContent;
248 if ( !textContent.empty() && !textContent.ends_with("\n") ) {
249 xmlString += "\n";
250 }
251 xmlString += indent(depth) + std::string("</") + (!prefix.empty() ? prefix + ":" : "") + elementName + ">\n";
252 return xmlString;
253}
254
255std::ostream& operator<< (std::ostream& os, const XMLObject* obj) {
256 os << obj->stringify();
257 return os;
258}
259
260std::ostream& operator<< (std::ostream& os, const XMLObject& obj) {
261 os << obj.stringify();
262 return os;
263}
264
265} // end namespace XML
266
A class representing a node in an XML-tree.
Definition XMLObject.h:115
Attributes attributes
Definition XMLObject.h:249
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)
std::optional< std::reference_wrapper< XMLObject > > getOptionalChildByName(const ElementName &name)
Get the optional child with the specified element name.
ElementName elementName
Definition XMLObject.h:245
std::string format(std::string indentation="\t", unsigned int depth=0) const
Creates formatted string representing the XMLObject including its children.
std::optional< std::reference_wrapper< Attribute > > getOptionalAttributeByName(const AttributeName &name)
Get an optional attribute with the specified attribute name.
Children children
Child nodes of the XML element.
Definition XMLObject.h:248
Namespace prefix
Definition XMLObject.h:244
XMLObject & getRequiredChildByName(const ElementName &name)
Get a required child with the specified element name.
TextContent textContent
Textual content of XML element without children.
Definition XMLObject.h:247
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)
Attribute & getRequiredAttributeByName(const AttributeName &name)
Get a required attribute with the specified attribute name.
static XMLObject * createFromStream(std::istream &xmlStream)
Create an XMLObject from the input stream.
Definition XMLObject.cpp:47
std::vector< std::reference_wrapper< XMLObject > > getChildrenByName(const ElementName &name)
Get all children with the specified element name.
static Factory factory
Definition XMLObject.h:155
Namespace xmlns
Definition XMLObject.h:242
The XML namespace contains classes representing XML-nodes defined in given XML-schema(s).
Definition XMLObject.cpp:9
std::string ElementName
Definition XMLObject.h:22
std::string Namespace
Definition XMLObject.h:24
std::ostream & operator<<(std::ostream &os, const XMLObject *obj)
Allows printing of stringified XML object.
std::vector< Attribute > Attributes
Definition XMLObject.h:80
std::string ClassName
Definition XMLObject.h:21
std::string AttributeName
Definition XMLObject.h:25
std::string transcode(const XMLCh *xmlChStr)
Definition XMLObject.cpp:37
A struct representing an attribute of an XML-node.
Definition XMLObject.h:73
A struct representing the value of an XML-node attribute.
Definition XMLObject.h:48