PLCnext API Documentation 24.0.0.71
XmlReader.hpp
1
2//
3// Copyright PHOENIX CONTACT Electronics GmbH
4//
6#pragma once
8#include "Arp/System/Core/Enum.hxx"
9#include "Arp/System/Core/TypeName.hxx"
10#include "Arp/System/Commons/Chrono/Duration.hpp"
11#include "Arp/System/Commons/Exceptions/Exceptions.h"
12#include "Arp/System/Commons/Xml/XmlErrorHandler.hpp"
13#include "Arp/System/Commons/Xml/XmlNodeType.hpp"
14
15#include <sstream>
16#include "boost/lexical_cast.hpp"
17
18namespace Arp { namespace System { namespace Commons { namespace Xml
19{
20
21class LibXmlErrorHandler;
22
25{
26public: // typedefs
27public: // construction/destruction
29 XmlReader(void);
34
35private: // construction/destruction
36 XmlReader(void* pReader);
37 XmlReader(const XmlReader& arg) = delete;
38 XmlReader& operator=(const XmlReader& arg) = delete;
39
40public: // static factory operations
47 static XmlReader Create(const String& filename);
48
55 static bool TryCreate(const String& filename, XmlReader& reader);
56
70 static XmlReader Create(const String& filename, const String& xsdFile);
71
72
80 static XmlReader CreateForString(const String& buffer);
81
82public: // setter/getter operations
90
98
106
107public: // operations
114 bool Read();
115
122 XmlNodeType GetCurrentNodeType();
130
145 bool Skip(); // TODO: remove this due to unclear naming
153
162
163 // moves to the element that contains the current attribute
164
173 // moves to the next element with name
174
184 bool ReadToFollowing(const String& name);
185 // reads to the next element with name included in current element
186
198 bool ReadToDescendant(const String& name);
199 // skips the current element and reads until the next sibling with name
200
211 bool ReadToNextSibling(const String& name);
212
213 // reads to the next start element and tests the name against name, not needed after
214 // ReadEndElement
215
223 void ReadStartElement(const char* elementName = nullptr);
224
235 bool TryReadStartElement(const char* elementName = nullptr);
245 bool TryReadStartElement(String& elementName, bool conditioned = true);
246
251 void ReadEndElement(void);
268 // reads the content as CData section of the current element
269 // read CDATA is not required, ReadElementContent removes CDATA Tags automatically
270 // String ReadElementContentCData(void);
271 // bool TryReadElementContentCData(String& result);
272
280 template<class T>
282 {
283 String elementString = this->ReadElementContent();
284 try
285 {
286 return boost::lexical_cast<T>(elementString);
287 }
288 catch (std::exception& e)
289 {
291 this->GetDocumentFileName(),
292 this->GetLineNumber(),
293 "Cannot convert '{}' to {}: {}", elementString, TypeName<T>().GetFullName(), e.what());
294 }
295 }
296
308 template<class T>
309 T GetAttributeValue(const char* attributeName)
310 {
311 T returnValue;
312 if (TryGetAttributeValue<T>(attributeName, returnValue))
313 {
314 return returnValue;
315 }
316 else
317 {
318 throw XmlException(
319 "GetAttributeValue failed for mandatory attribute: {0} elementName: {1}",
320 attributeName, GetElementLocalName());
321 }
322 }
323
335 template<class T>
336 Enum<T> GetAttributeValueEnum(const char* attributeName)
337 {
338 Enum<T> returnValue;
339
340 if (TryGetAttributeValueEnum<T>(attributeName, returnValue))
341 {
342 return returnValue;
343 }
344 else
345 {
346 throw XmlException(
347 "GetAttributeValueEnum failed for mandatory attribute: {0} elementName: {1}",
348 attributeName, GetElementLocalName());
349 }
350 }
351
363 template<class T>
364 T GetAttributeValueOfEnum(const char* attributeName)
365 {
366 T result;
367 if (!TryGetAttributeValueEnum<T>(attributeName, result))
368 {
369 throw XmlException(
370 "GetAttributeValueOfEnum failed for mandatory attribute: {0} elementName: {1}", attributeName, GetElementLocalName());
371 }
372 return result;
373 }
374
389 template<class T>
390 bool TryGetAttributeValueEnum(const char* attributeName, T& value)
391 {
392 char* charValue = GetAttributeValueInternal(attributeName);
393 if (charValue == nullptr)
394 {
395 return false;
396 }
397 std::istringstream iss(charValue);
398 iss >> value;
399 FreeInternalAttributeValue(charValue);
400 return true;
401 }
402
417 template<class T>
418 bool TryGetAttributeValueEnum(const char* attributeName, Enum<T>& value)
419 {
420 char* charValue = GetAttributeValueInternal(attributeName);
421 if (charValue == nullptr)
422 {
423 return false;
424 }
425
426 value = Enum<T>::Parse(charValue);
427 FreeInternalAttributeValue(charValue);
428 return true;
429 }
430
448 template<class T>
449 bool TryGetAttributeValueEnum(const char* attributeName, T defaultValue, Enum<T>& value)
450 {
451 value = defaultValue;
452 return TryGetAttributeValueEnum<T>(attributeName, value);
453 }
454
455
468 template<class T>
469 T GetAttributeValueDuration(const char* attributeName)
470 {
471 T returnValue;
472
473 if (TryGetAttributeValueDuration<T>(attributeName, returnValue))
474 {
475 return returnValue;
476 }
477 else
478 {
479 throw XmlException(
480 "GetAttributeValueDuration failed for mandatory attribute: {0} elementName: {1}",
481 attributeName, GetElementLocalName());
482 }
483 }
497 template<class T>
498 bool TryGetAttributeValueDuration(const char* attributeName, T& value)
499 {
500 char* charValue = GetAttributeValueInternal(attributeName);
501 Chrono::Duration tempDurationValue;
502
503 if (charValue == nullptr)
504 {
505 return false;
506 }
507
508 bool result = Chrono::Duration::TryParse(charValue, tempDurationValue);
509 if (!result)
510 {
511 throw XmlException(
512 "TryGetAttributeValueDuration failed for attribute '{0}' in element '{1}': invalid duration format in value '{2}'",
513 attributeName, GetElementLocalName(), charValue);
514 }
515 tempDurationValue.GetDuration(value);
516
517 FreeInternalAttributeValue(charValue);
518 return result;
519 }
520
538 template<class T>
539 bool TryGetAttributeValueDuration(const char* attributeName, T defaultValue, T& value)
540 {
541 value = defaultValue;
542 return TryGetAttributeValueDuration<T>(attributeName, value);
543 }
544
545
559 template<class T>
560 bool TryGetAttributeValue(const char* attributeName, T& value)
561 {
562 char* charValue = nullptr;
563 try
564 {
565 charValue = GetAttributeValueInternal(attributeName);
566 if (charValue == nullptr)
567 {
568 return false;
569 }
570
571 // boost::lexical_cast seems fast enough:
572 // http://stackoverflow.com/questions/23582089/is-boostlexical-cast-redundant-with-c11-stoi-stof-and-family
573 // boost::lexical_cast is 10 times slower than atoi on Linux but has more error checks
574 // http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/
575 value = boost::lexical_cast<T>(charValue);
576 FreeInternalAttributeValue(charValue);
577 return true;
578 }
579 catch (boost::bad_lexical_cast&)
580 {
581
582 String strValue(charValue);
583 FreeInternalAttributeValue(charValue);
584 throw XmlException("Cast of attribute {0} to {1} failed value: {2}", attributeName,
585 TypeName<T>(), strValue);
586 }
587 }
588
589
607 template<class T>
608 bool TryGetAttributeValue(const char* attributeName, T defaultValue, T& value)
609 {
610
611 value = defaultValue;
612 return TryGetAttributeValue<T>(attributeName, value);
613 }
614
615 void EnableValidationException();
616 void DisableValidationException();
617
627 template<typename... Args>
628 XmlException CreateException(const char* message, const Args& ... args);
629
630protected: // operations
631private: // static methods
632private: // methods
633 int ReaderState();
634 char* GetAttributeValueInternal(const char* attributeName);
635 void FreeInternalAttributeValue(char* attributeValue);
636 // Move to the content node, if the element is not an content node
637 // content nodes are: non-whitespace text, CDATA, Element, EndElement, EntityReference,
638 // EndEntity
639 XmlNodeType MoveToContent();
640 void EnableValidation(const String& xsdFile);
641 void CheckForValidationException();
642
643private: // fields
644 String filename;
645 // internal reader ptr
646 void* reader;
647 bool validationEnabled;
648 LibXmlErrorHandler* errorHandler;
649 bool exceptionOnValidationError; // defines whether to throw an exception when a validation
650 // error occurs while parsing
651 String lastError;
652 bool startElementRead;
653
654private: // static fields
655};
656
657// gcc does not allow full template specialization in class definition
658// boost::lexical_cast does not understand true and false so implement special version
659template<>
660bool XmlReader::TryGetAttributeValue(const char*, bool& value);
661
662template<>
663bool XmlReader::TryGetAttributeValue(const char* attributeName, String& value);
664
665template<>
666bool XmlReader::TryGetAttributeValue(const char* attributeName, Chrono::Duration& value);
667
668template<>
669bool XmlReader::TryGetAttributeValue(const char* attributeName, uint8& value);
670
671template<>
672bool XmlReader::TryGetAttributeValue(const char* attributeName, int8& value);
673
675// inline methods of class XmlReader
676template<typename... Args>
677inline XmlException XmlReader::CreateException(const char* message, const Args& ... args)
678{
679 return XmlException::Create(this->filename, this->GetLineNumber(), message, args...);
680}
681
683{
684 return this->filename;
685}
686
687}}}} // namespace Arp::System::Commons::Xml
Adapter class for enums to make them loggable and parsable from e.g. XML files.
Definition: Enum.hxx:23
Definition: Duration.hpp:16
This exception is used for xml parsing errors.
Definition: XmlException.hpp:15
static XmlException Create(void)
Creates an XmlException instance using a default message text.
Definition: XmlException.hpp:79
internal class to handle xml parsing errors in XmlReader
Definition: LibXmlErrorHandler.hpp:16
Class to read an XML File. Non buffered reader, can only read forward
Definition: XmlReader.hpp:25
bool TryGetAttributeValueEnum(const char *attributeName, T &value)
trys reads an attribute value from the current element and converts the String to Enum<T>
Definition: XmlReader.hpp:390
XmlException CreateException(const char *message, const Args &... args)
Creates an exception by prefixing the given message with current doc name and line number
Definition: XmlReader.hpp:677
T ReadElementContent()
reads the content of the current element if a CDATA section is found, the CDATA enclosure is removed
Definition: XmlReader.hpp:281
static XmlReader CreateForString(const String &buffer)
Creates a new Instance of the XmlReader reading from an in memory buffer
T GetAttributeValue(const char *attributeName)
reads an attribute value from the current element
Definition: XmlReader.hpp:309
int GetCurrentDepth()
gets the current depth of the current element inside the xml document
bool Read()
Reads to the next element inside the xml file
static bool TryCreate(const String &filename, XmlReader &reader)
Tries to creates a new Instance of the XmlReader
Enum< T > GetAttributeValueEnum(const char *attributeName)
reads an attribute value from the current element and converts the String to Enum<T>
Definition: XmlReader.hpp:336
static XmlReader Create(const String &filename, const String &xsdFile)
Creates a new Instance of the XmlReader and enables Schema validation with the XSD schema at xsdFile
XmlNodeType GetCurrentNodeType()
gets the type of the current xml element
bool ReadToNextSibling(const String &name)
reads to the next element with name which is a sibling of the current element
bool IsEmptyElement()
test if the current element is an empty element
bool TryGetAttributeValue(const char *attributeName, T defaultValue, T &value)
tries to read an attribute value from the current element if the attribute was not found or the conve...
Definition: XmlReader.hpp:608
bool SkipElement()
skips the current element with its whole subtree
bool TryGetAttributeValueDuration(const char *attributeName, T defaultValue, T &value)
tries to read an attribute value from the current element as std::crono::duration if the attribute wa...
Definition: XmlReader.hpp:539
XmlReader(void)
Default contructor.
bool TryGetAttributeValueEnum(const char *attributeName, Enum< T > &value)
trys reads an attribute value from the current element and converts the String to Enum<T>
Definition: XmlReader.hpp:418
void ReadStartElement(const char *elementName=nullptr)
reads to the next element and test if this is a start element with the given name
XmlReader(XmlReader &&arg)
Move contructor.
static XmlReader Create(const String &filename)
Creates a new Instance of the XmlReader
bool TryReadStartElement(const char *elementName=nullptr)
trys to read the next element and test if this is a start element with the given name
bool MoveToElement()
Moves the position of the current instance to the node that contains the current Attribute node.
T GetAttributeValueOfEnum(const char *attributeName)
reads an attribute value from the current element and converts the String to Enum<T>
Definition: XmlReader.hpp:364
const String & GetDocumentFileName()
Gets the current document filename of this XML reader
Definition: XmlReader.hpp:682
void ReadEndElement(void)
reads until the next end element is reached skips over an empty element
T GetAttributeValueDuration(const char *attributeName)
reads an attribute value from the current element and converts the String to std::chrono::duration i....
Definition: XmlReader.hpp:469
bool ReadToDescendant(const String &name)
reads the xml file until an element with name is reached or the current element is closed reads to th...
bool Skip()
skips the current element with its whole subtree (use SkipElement() because naming is more clear)
bool SkipElementContent()
skips the content of current element with its whole subtree but does not read end element tag
String ReadElementContent(void)
reads the content of the current element if a CDATA section is found, the CDATA enclosure is removed
~XmlReader(void)
Destructs this instance and frees all resources.
bool TryGetAttributeValueEnum(const char *attributeName, T defaultValue, Enum< T > &value)
tries to read an attribute value from the current element as enum if the attribute was not found or t...
Definition: XmlReader.hpp:449
bool TryReadStartElement(String &elementName, bool conditioned=true)
trys to read the next element and returns the read element name as out argument
long GetLineNumber()
Gets the actual line number of this XML reader
String GetElementLocalName(void)
Reads the local name of the current active element
bool TryReadEndElement(void)
tries to read until the next end element is reached skips over an empty element
bool ReadToFollowing(const String &name)
reads the xml file until an element with name is reached or end of file is reached
bool TryGetAttributeValueDuration(const char *attributeName, T &value)
trys reads an attribute value from the current element and converts the String to std::chrono::durati...
Definition: XmlReader.hpp:498
bool TryGetAttributeValue(const char *attributeName, T &value)
tries to read an attribute value from the current element
Definition: XmlReader.hpp:560
This (meta programming) class provides the C++ typename of the as template argument passed type.
Definition: TypeName.hxx:67
static Enum Parse(const String &input)
Parses the given input string.
Definition: Enum.hxx:228
std::int8_t int8
The Arp integer type of 1 byte size.
Definition: PrimitiveTypes.hpp:30
std::uint8_t uint8
The Arp unsigned integer type of 1 byte size.
Definition: PrimitiveTypes.hpp:28
@ System
System components used by the System, Device, Plc or Io domains.
Root namespace for the PLCnext API