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