How To Use
 All Modules Pages
PugiXml - reading, writing, searching XML data

Modules

 C++ Example for using PugiXml
 

Detailed Description

Overview:

Introduction

PugiXml is a C++ open source library realizing a very fast XML parser; for navigation and selection within XML trees you can also use XPath expressions. PugiXml is available at http://pugixml.org/

In contrast to other XML parsers (e.g. TinyXml) you have to pay attention to the following aspects:

Create, load and save an xml file

Create new xml file

Construct your XML tree within an "xml_document" and store it within an XML file using "save_file":

// Generate new XML document within memory
pugi::xml_document doc;
// Alternatively store as shared pointer if tree shall be used for longer
// time or multiple client calls:
// std::shared_ptr<pugi::xml_document> spDoc = std::make_shared<pugi::xml_document>();
// Generate XML declaration
auto declarationNode = doc.append_child(pugi::node_declaration);
declarationNode.append_attribute("version") = "1.0";
declarationNode.append_attribute("encoding") = "ISO-8859-1";
declarationNode.append_attribute("standalone") = "yes";
// A valid XML doc must contain a single root node of any name
auto root = doc.append_child("MyRoot");
// Save XML tree to file.
// Remark: second optional param is indent string to be used;
// default indentation is tab character.
bool saveSucceeded = doc.save_file(XML_FILE_PATH.c_str(), PUGIXML_TEXT(" "));
assert(saveSucceeded);

Load existing xml file

Load the contents of an existing XML file into an xml_document by calling "load_file":

// Create empty XML document within memory
pugi::xml_document doc;
// Load XML file into memory
// Remark: to fully read declaration entries you have to specify
// "pugi::parse_declaration"
pugi::xml_parse_result result = doc.load_file(XML_FILE_PATH.c_str(),
pugi::parse_default|pugi::parse_declaration);
if (!result)
{
std::cout << "Parse error: " << result.description()
<< ", character pos= " << result.offset;
}
// A valid XML document must have a single root node
pugi::xml_node root = doc.document_element();

Add data (nodes and attributes) to existing xml tree

Add child elements with attributes

In the simplest case child elements can be added at top ("prepend") or at end ("append") of possibly already existing child elements. You can assign attribute values of any basic type:

// Append some child elements below root
// Add as last element
pugi::xml_node nodeChild = root.append_child("MyChild");
nodeChild.append_attribute("hint") = "inserted as last child";
nodeChild.append_attribute("intVal") = in_intVal;
// Add as last element
nodeChild = root.append_child("MyChild");
nodeChild.append_attribute("hint") = "also inserted as last child";
nodeChild.append_attribute("doubleVal") = in_doubleVal;
// Add as first element
nodeChild = root.prepend_child("MyChild");
nodeChild.append_attribute("hint") = "inserted at front";
nodeChild.append_attribute("boolVal") = in_boolVal;

Add child elements with direct values

Instead of storing some value within an attribute you also can store values within the node itself:

pugi::xml_node childrenWithValues = root.append_child("ChildrenWithValue");
// Add child of type integer
pugi::xml_node nodeChild = childrenWithValues.append_child("MyChildWithIntValue");
nodeChild.append_child(pugi::node_pcdata).set_value(ToString(4712).c_str());
// Add child of type double
nodeChild = childrenWithValues.append_child("MyChildWithDoubleValue");
nodeChild.append_child(pugi::node_pcdata).set_value(ToString(3.18).c_str());
// Add child of type bool
nodeChild = childrenWithValues.append_child("MyChildWithBoolValue");
nodeChild.append_child(pugi::node_pcdata).set_value(ToString(false).c_str());

Helper function for type conversion

// Helper functions to uniformly convert data types to string representation
template <typename T>
std::string ToString(T const & in_val)
{
return std::to_string(in_val);
}
// Specialization for boolean type to force "true"/"false"
template<>
std::string ToString(bool const & in_val)
{
std::ostringstream oss;
oss << std::boolalpha << in_val;
return oss.str();
}

Add external xml subtree (e.g. given in string format) to existing xml tree

You can add an external xml string to your xml_document by loading it into a temporary xml_document and copying it into a node of your target xml_document by using "append_copy":

std::string externalXmlString = "<ExternalData>"
"<X>-1.5</X>"
"<NumPoints>23</NumPoints>"
"<exists>true</exists>"
"<SomeChild intVal=\"1508\" doubleVal=\"4.5\" boolVal=\"false\"/>"
"</ExternalData>";
// Read XML string into temporary document
pugi::xml_document tmpDoc;
if (tmpDoc.load(externalXmlString.c_str()))
{
// Create child node to hold external XML data
pugi::xml_node childWithExternalSubtree = root.append_child("ChildWithExternalXmlSubtree");
// Copy subtree from temporary document to target node
childWithExternalSubtree.append_copy(tmpDoc.document_element());
}

Example XML file as generated from code snippets

The sections above have generated the following xml contents:

R"(<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<MyRoot>
<MyChild hint="inserted at front" boolVal="false" />
<MyChild hint="inserted at front" boolVal="true" />
<MyChild hint="inserted as last child" intVal="4711" />
<MyChild hint="also inserted as last child" doubleVal="3.14" />
<MyChild hint="inserted as last child" intVal="4712" />
<MyChild hint="also inserted as last child" doubleVal="3.15" />
<ChildrenWithValue>
<MyChildWithIntValue>4712</MyChildWithIntValue>
<MyChildWithDoubleValue>3.180000</MyChildWithDoubleValue>
<MyChildWithBoolValue>false</MyChildWithBoolValue>
</ChildrenWithValue>
<ChildWithExternalXmlSubtree>
<ExternalData>
<X>-1.5</X>
<NumPoints>23</NumPoints>
<exists>true</exists>
<SomeChild intVal="1508" doubleVal="4.5" boolVal="false" />
</ExternalData>
</ChildWithExternalXmlSubtree>
</MyRoot>
)";

This file contents will be used in the following code samples to demonstrate reading and searching of data.

Read attribute and node values

Check for attribute existence and read attribute value

If a given attribute exists you can read it as string or numeric type:

// Read attribute value
pugi::xml_attribute attr;
if (attr = selectedNode.attribute("intVal")) // attribute really exists
{
// Read value as string
std::cout << "read as string: intVal=" << attr.value() << std::endl;
// Read value as int
int intVal = attr.as_int();
std::cout << "read as int : intVal=" << intVal << std::endl;
// for other types use as_double, as_bool, as_uint, ...
}

Read attribute values as numeric types

Each attribute value of suitable text format can be interpreted as a numeric type:

// Read various attribute types of "SomeChild" according to their expected types
double doubleVal = selectedNode.child("SomeChild").attribute("doubleVal").as_double();
int intVal = selectedNode.child("SomeChild").attribute("intVal").as_int();
bool boolVal = selectedNode.child("SomeChild").attribute("boolVal").as_bool();

Read node values as numeric types

Each node value of suitable text format can be interpreted as a numeric type:

// Read several child node values according to their expected types
double x = selectedNode.child("X") .text().as_double();
int numPoints = selectedNode.child("NumPoints").text().as_int();
bool exists = selectedNode.child("exists") .text().as_bool();

Searching with XPath

XPath: search node with given name

Look for the first node of given name which has the given attribute value:

// Search for the first matching entry with the given hint attribute
std::string searchStr = "ChildWithExternalXmlSubtree/ExternalData";
pugi::xpath_node xpathNode = root.select_single_node(searchStr.c_str());
if (xpathNode)
{
pugi::xml_node selectedNode = xpathNode.node();
// continue with accessing node
// ...

XPath: search first/last node with given attribute value

Look for the first or last node of given name which has the given attribute value:

// Search for the first / last child entry with the given hint attribute
std::string searchStr = in_searchFirst ? "MyChild[@hint='inserted as last child']"
: "MyChild[@hint='inserted as last child'][last()]";
pugi::xpath_node xpathNode = root.select_single_node(searchStr.c_str());
if (xpathNode)
{
pugi::xml_node selectedNode = xpathNode.node();
// now access found node
// ...

Write xml data to a stream

Instead of saving the XML document to a file you can also write the whole document to a stream by using "save":

// Write complete xml document to stdout
std::cout << "\nWrite xml doc to stdout with indent of 1 char:" << std::endl;
doc.save(std::cout," ");
// Write complete xml document to string stream
std::cout << "\nWrite xml doc to stringstream with indent of 2 chars:" << std::endl;
std::stringstream ss;
doc.save(ss," ");
std::cout << "stream contents are:\n" << ss.str() << std::endl;

With use of "print" any xml subtree can be written to a stream:

// Write xml subtree to stdout
std::cout << "\nWrite subtree to stdout with indent of 1 char:" << std::endl;
root.child("ChildWithExternalXmlSubtree").print(std::cout, " ");
// Write xml subtree to string stream
std::cout << "\nWrite subtree to stringstream with indent of 2 chars:" << std::endl;
std::stringstream ss;
root.child("ChildWithExternalXmlSubtree").print(ss, " ");
std::cout << "stream contents are:\n" << ss.str() << std::endl;

Remove attributes

You can simply remove an attribute of an XMl node by specifying its name:

someNode.remove_attribute("x");

When iterating through attributes you can selectively decide to remove an attribute:

for (pugi::xml_attribute attr = someNode.first_attribute(); attr;)
{
// Get next attribute before possibly deleting current attribute
pugi::xml_attribute nextAttr = attr.next_attribute();
// Check wether attribute shall be deleted
if ((std::string(attr.name())=="phi") || (attr.as_double() < 0))
{
someNode.remove_attribute(attr);
}
attr = nextAttr;
}

Remove child nodes

You can simply remove all child nodes of a given parent node by specifying the child name:

// "remove_child" only removes the first found child
// the return value signals if a child was found
// removing all childs with a given name:
while(someParentNode.remove_child("AnOtherNode"));

When iterating through child nodes you can selectively decide to remove a child:

for (pugi::xml_node child = someParentNode.first_child(); child; )
{
// Get next child node before possibly deleting current child
pugi::xml_node next = child.next_sibling();
// Check wether child node shall be deleted
if (std::string(child.name()) != "AnOtherNode")
{
child.parent().remove_child(child);
}
child = next;
}

References