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
165// getRequiredChildByName / getOptionalChildByName / getChildrenByName /
166// getRequiredAttributeByName / getOptionalAttributeByName are now defined inline in the
167// header as C++23 deducing-this templates so they are const-correct with the receiver.
168
169std::string XMLObject::stringify() const {
170 std::string xmlString = std::string("<") + (!prefix.empty() ? prefix + ":" : "") + elementName;
171 for ( auto& attribute : attributes ) {
172 xmlString += std::string(" ") + (!attribute.prefix.empty() ? attribute.prefix + ":" : "") + attribute.name + "=\"" + attribute.value.value +"\"";
173 }
174 xmlString += ">";
175
176 for ( auto& child : children ) {
177 xmlString += child->stringify();
178 }
179 xmlString += textContent;
180 xmlString += std::string("</") + (!prefix.empty() ? prefix + ":" : "") + elementName + ">";
181 return xmlString;
182}
183
184std::string XMLObject::format(std::string indentation, unsigned int depth) const {
185 // lambda to repeat indentation n times
186 auto indent = [&indentation](unsigned int n) -> std::string {
187 std::string result;
188 for (unsigned int i = 0; i < n; ++i) {
189 result += indentation;
190 }
191 return result;
192 };
193
194 std::string xmlString = indent(depth) + std::string("<") + (!prefix.empty() ? prefix + ":" : "") + elementName;
195 for ( auto& attribute : attributes ) {
196 xmlString += std::string(" ") + (!attribute.prefix.empty() ? attribute.prefix + ":" : "") + attribute.name + "=\"" + attribute.value.value +"\"";
197 }
198 xmlString += ">\n";
199
200 for ( auto& child : children ) {
201 xmlString += child->format(indentation, depth+1);
202 }
203 xmlString += textContent;
204 if ( !textContent.empty() && !textContent.ends_with("\n") ) {
205 xmlString += "\n";
206 }
207 xmlString += indent(depth) + std::string("</") + (!prefix.empty() ? prefix + ":" : "") + elementName + ">\n";
208 return xmlString;
209}
210
211std::ostream& operator<< (std::ostream& os, const XMLObject* obj) {
212 os << obj->stringify();
213 return os;
214}
215
216std::ostream& operator<< (std::ostream& os, const XMLObject& obj) {
217 os << obj.stringify();
218 return os;
219}
220
221} // end namespace XML
222
A class representing a node in an XML-tree.
Definition XMLObject.h:129
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)
ElementName elementName
Definition XMLObject.h:222
const ClassName className
Definition XMLObject.h:220
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
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
static XMLObject * createFromStream(std::istream &xmlStream)
Create an XMLObject from the input stream.
Definition XMLObject.cpp:47
static Factory factory
Definition XMLObject.h:169
Namespace xmlns
Definition XMLObject.h:219
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::string ClassName
Definition XMLObject.h:23
std::string AttributeName
Definition XMLObject.h:27
std::string transcode(const XMLCh *xmlChStr)
Definition XMLObject.cpp:37
A struct representing the value of an XML-node attribute.
Definition XMLObject.h:50