#include "stdafx.h"
#include "MyTestCommands.h"
#include <pugixml.hpp>
#include <TestToolBox\TestToolBox.h>
#include <boost/test/unit_test.hpp>
namespace TTB = TestToolBox;
std::string const XML_FILE_PATH = "C://MyTemp/PugiXml/MyDemo.xml";
void CreateNewXmlFileWithEmptyRootNode()
{
pugi::xml_document doc;
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";
auto root = doc.append_child("MyRoot");
bool saveSucceeded = doc.save_file(XML_FILE_PATH.c_str(), PUGIXML_TEXT(" "));
assert(saveSucceeded);
}
void AddSomeChildNodesWithVariousAttributeTypesToExistingXmlFile(
int in_intVal,
double in_doubleVal,
bool in_boolVal)
{
pugi::xml_document doc;
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;
}
pugi::xml_node root = doc.document_element();
pugi::xml_node nodeChild = root.append_child("MyChild");
nodeChild.append_attribute("hint") = "inserted as last child";
nodeChild.append_attribute("intVal") = in_intVal;
nodeChild = root.append_child("MyChild");
nodeChild.append_attribute("hint") = "also inserted as last child";
nodeChild.append_attribute("doubleVal") = in_doubleVal;
nodeChild = root.prepend_child("MyChild");
nodeChild.append_attribute("hint") = "inserted at front";
nodeChild.append_attribute("boolVal") = in_boolVal;
bool saveSucceeded = doc.save_file(XML_FILE_PATH.c_str(), PUGIXML_TEXT(" "));
assert(saveSucceeded);
}
template <typename T>
std::string ToString(T const & in_val)
{
return std::to_string(in_val);
}
template<>
std::string ToString(bool const & in_val)
{
std::ostringstream oss;
oss << std::boolalpha << in_val;
return oss.str();
}
void AddSomeChildNodesWithDirectValuesToExistingXmlFile()
{
pugi::xml_document doc;
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;
}
pugi::xml_node root = doc.document_element();
pugi::xml_node childrenWithValues = root.append_child("ChildrenWithValue");
pugi::xml_node nodeChild = childrenWithValues.append_child("MyChildWithIntValue");
nodeChild.append_child(pugi::node_pcdata).set_value(ToString(4712).c_str());
nodeChild = childrenWithValues.append_child("MyChildWithDoubleValue");
nodeChild.append_child(pugi::node_pcdata).set_value(ToString(3.18).c_str());
nodeChild = childrenWithValues.append_child("MyChildWithBoolValue");
nodeChild.append_child(pugi::node_pcdata).set_value(ToString(false).c_str());
bool saveSucceeded = doc.save_file(XML_FILE_PATH.c_str(), PUGIXML_TEXT(" "));
assert(saveSucceeded);
}
void AddXmlSubtreeToExistingXmlFile()
{
pugi::xml_document doc;
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;
}
pugi::xml_node root = doc.document_element();
std::string externalXmlString = "<ExternalData>"
"<X>-1.5</X>"
"<NumPoints>23</NumPoints>"
"<exists>true</exists>"
"<SomeChild intVal=\"1508\" doubleVal=\"4.5\" boolVal=\"false\"/>"
"</ExternalData>";
pugi::xml_document tmpDoc;
if (tmpDoc.load(externalXmlString.c_str()))
{
pugi::xml_node childWithExternalSubtree = root.append_child("ChildWithExternalXmlSubtree");
childWithExternalSubtree.append_copy(tmpDoc.document_element());
}
bool saveSucceeded = doc.save_file(XML_FILE_PATH.c_str(), PUGIXML_TEXT(" "));
assert(saveSucceeded);
}
void SearchForNodesUsingXpath(bool in_searchFirst)
{
pugi::xml_document doc;
doc.load_file(XML_FILE_PATH.c_str());
pugi::xml_node root = doc.document_element();
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();
pugi::xml_attribute attr;
if (attr = selectedNode.attribute("intVal"))
{
std::cout << "read as string: intVal=" << attr.value() << std::endl;
int intVal = attr.as_int();
std::cout << "read as int : intVal=" << intVal << std::endl;
}
}
}
void ReadVariousAttributeAndNodeTypes()
{
pugi::xml_document doc;
doc.load_file(XML_FILE_PATH.c_str());
pugi::xml_node root = doc.document_element();
std::string searchStr = "ChildWithExternalXmlSubtree/ExternalData";
pugi::xpath_node xpathNode = root.select_single_node(searchStr.c_str());
if (xpathNode)
{
pugi::xml_node selectedNode = xpathNode.node();
double x = selectedNode.child("X") .text().as_double();
int numPoints = selectedNode.child("NumPoints").text().as_int();
bool exists = selectedNode.child("exists") .text().as_bool();
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();
std::cout << "\nnode values read: X=" << x
<< " NumPoints=" << numPoints
<< " exists=" << std::boolalpha << exists << std::endl;
std::cout << "attribute values read: doubleVal=" << doubleVal
<< " intVal=" << intVal
<< " boolVal=" << std::boolalpha << boolVal << std::endl;
}
}
void WriteXmlDocumentToStream()
{
pugi::xml_document doc;
doc.load_file(XML_FILE_PATH.c_str(), pugi::parse_default|pugi::parse_declaration);
std::cout << "\nWrite xml doc to stdout with indent of 1 char:" << std::endl;
doc.save(std::cout," ");
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;
std::string generatedXmlContents = ss.str();
std::string expectedOutput =
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>
)";
assert(generatedXmlContents == expectedOutput);
}
void WriteXmlSubTreeToStream()
{
pugi::xml_document doc;
doc.load_file(XML_FILE_PATH.c_str(), pugi::parse_default | pugi::parse_declaration);
pugi::xml_node root = doc.document_element();
std::cout << "\nWrite subtree to stdout with indent of 1 char:" << std::endl;
root.child("ChildWithExternalXmlSubtree").print(std::cout, " ");
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;
std::string generatedXmlContents = ss.str();
std::string expectedOutput =
R"(<ChildWithExternalXmlSubtree>
<ExternalData>
<X>-1.5</X>
<NumPoints>23</NumPoints>
<exists>true</exists>
<SomeChild intVal="1508" doubleVal="4.5" boolVal="false" />
</ExternalData>
</ChildWithExternalXmlSubtree>
)";
assert(generatedXmlContents == expectedOutput);
}
void RemoveAttributes()
{
std::string xmlString = R"(<SomeNode x="0.5" y="2.18" z="-3.5" phi="20">)";
pugi::xml_document doc;
doc.load(xmlString.c_str());
pugi::xml_node someNode = doc.document_element();
{
std::cout << "\nWrite tree before deletion of attributes" << std::endl;
std::stringstream ss;
someNode.print(ss, " ");
std::cout << ss.str() << std::endl;
std::string generatedXmlContents = ss.str();
std::string expectedOutput = R"(<SomeNode x="0.5" y="2.18" z="-3.5" phi="20" />)" "\n";
assert(generatedXmlContents == expectedOutput);
}
for (pugi::xml_attribute attr = someNode.first_attribute(); attr;)
{
pugi::xml_attribute nextAttr = attr.next_attribute();
if ((std::string(attr.name())=="phi") || (attr.as_double() < 0))
{
someNode.remove_attribute(attr);
}
attr = nextAttr;
}
{
std::cout << "\nWrite tree after deletion of attributes" << std::endl;
std::stringstream ss;
someNode.print(ss, " ");
std::cout << ss.str() << std::endl;
std::string generatedXmlContents = ss.str();
std::string expectedOutput = R"(<SomeNode x="0.5" y="2.18" />)" "\n";
assert(generatedXmlContents == expectedOutput);
}
someNode.remove_attribute("x");
{
std::cout << "\nWrite tree after second deletion of attributes" << std::endl;
std::stringstream ss;
someNode.print(ss, " ");
std::cout << ss.str() << std::endl;
std::string generatedXmlContents = ss.str();
std::string expectedOutput = R"(<SomeNode y="2.18" />)" "\n";
assert(generatedXmlContents == expectedOutput);
}
}
void RemoveNodes()
{
std::string xmlString = R"(<Subtree>
<SomeNode>some content</SomeNode>
<OtherNode x="0.5" y="2.18" z="-3.5" phi="20" />
<AnOtherNode x="4.7" z="-3.5" />
<AnOtherNode z="22.3" />
<OtherNode x="0.5" y="2.18" z="-3.5" phi="20" />
</Subtree>)";
pugi::xml_document doc;
doc.load(xmlString.c_str());
pugi::xml_node someParentNode = doc.document_element();
{
std::cout << "\nWrite tree before deletion of child nodes" << std::endl;
std::stringstream ss;
someParentNode.print(ss, " ");
std::cout << ss.str() << std::endl;
std::string generatedXmlContents = ss.str();
std::string expectedOutput = xmlString + "\n";
assert(generatedXmlContents == expectedOutput);
}
for (pugi::xml_node child = someParentNode.first_child(); child; )
{
pugi::xml_node next = child.next_sibling();
if (std::string(child.name()) != "AnOtherNode")
{
child.parent().remove_child(child);
}
child = next;
}
{
std::cout << "\nWrite tree after deletion of child nodes" << std::endl;
std::stringstream ss;
someParentNode.print(ss, " ");
std::cout << ss.str() << std::endl;
std::string generatedXmlContents = ss.str();
std::string expectedOutput = R"(<Subtree>
<AnOtherNode x="4.7" z="-3.5" />
<AnOtherNode z="22.3" />
</Subtree>)" "\n";
assert(generatedXmlContents == expectedOutput);
}
while(someParentNode.remove_child("AnOtherNode"));
{
std::cout << "\nWrite tree after second deletion of child nodes" << std::endl;
std::stringstream ss;
someParentNode.print(ss, " ");
std::cout << ss.str() << std::endl;
std::string generatedXmlContents = ss.str();
std::string expectedOutput = R"(<Subtree />)" "\n";
assert(generatedXmlContents == expectedOutput);
}
}
void DemonstrateUsageOfPugiXml()
{
std::cout << "\n----------------------------" << std::endl;
std::cout << "\n\nHow to use PugiXml (no checks, simply write to stdout)" << std::endl;
CreateNewXmlFileWithEmptyRootNode();
AddSomeChildNodesWithVariousAttributeTypesToExistingXmlFile(4711,3.14,true);
AddSomeChildNodesWithVariousAttributeTypesToExistingXmlFile(4712,3.15,false);
AddSomeChildNodesWithDirectValuesToExistingXmlFile();
AddXmlSubtreeToExistingXmlFile();
SearchForNodesUsingXpath(true);
SearchForNodesUsingXpath(false);
ReadVariousAttributeAndNodeTypes();
WriteXmlDocumentToStream();
WriteXmlSubTreeToStream();
RemoveAttributes();
RemoveNodes();
std::cout << "\n----------------------------" << std::endl;
}
TTB_BOOST_TEST_CASE(Demo_HowToUsePugiXmlWithinProductionCode)
{
if (!TTB::TheEnvironment()->IsExistingCommandLineOption("-suppressDemoOutput"))
{
DemonstrateUsageOfPugiXml();
}
double valX = TTB::TheEnvironment()->GetCommandLineOptionVal<double>("-valX");
TTB_INFO_S("command line param valX=" << valX);
}}