In each of the examples below I have used the address example from the previous article as an example xml file to be read in and parsed (as is common with xml, especially configuration files), create an xml message from scratch, query the xml message and also output as xml to a stream or file. I have separated out the querying into an outputAddress function that will be used throughout the example.
Xsd
uses a data binding technique with generated code from an xsd schema file
a very small amount of code is required to effectively use the library
requires an xsd
uses generated code, which will need to be regenerated every time there is a schema change
// ---- parse from file ----boost::shared_ptr<Address>parsedFromFile;try{// read directly from file or uriparsedFromFile=Address_("address.xml");outputAddress(*parsedFromFile);}// all exceptions inherit from xml_schema::exception, can catch more specific if requiredcatch(constxml_schema::exception&e){std::cout<<e.what()<<std::endl;}// ---- create from scratch ----// the constructor takes in the amount of required fields in the xsd as argumentsAddressfromScratch("Mr. Malcolm Reynolds","3","Serenity","Space","DE18 5HI");// set valuesfromScratch.County("Solar System");fromScratch.Country("UK");std::stringstreamss;// ---- parse from stream ----xml_schema::namespace_infomapmap;map[""].schema="address.xsd";Address_(ss,fromScratch,map);boost::shared_ptr<Address>parsedFromStream(Address_(ss));outputAddress(*parsedFromStream);// ---- output to file ----std::ofstreamofs("test.xml");Address_(ofs,*parsedFromStream,map);
123456789
voidoutputAddress(constAddress&address){// output values using methods matching members in the xsdstd::cout<<address.Recipient()<<std::endl;std::cout<<address.House()<<" "<<address.Street()<<std::endl;std::cout<<address.Street()<<std::endl;std::cout<<address.Country()<<std::endl;std::cout<<address.PostCode()<<std::endl;}
// ---- parse from file and stream ----// need to convert file and stream to cstring before parsing// as rapidxml needs a null terminated cstring for parsing// file to stringstd::ifstreamfin("address.xml");std::stringstreamss;ss<<fin.rdbuf();std::stringxml=ss.str();// string to dynamic cstringstd::vector<char>stringCopy(xml.length(),'\0');std::copy(xml.begin(),xml.end(),stringCopy.begin());char*cstr=&stringCopy[0];// create xml document object and parse cstring// character type defaults to charrapidxml::xml_document<>parsedFromFile;// 0 means default parse flagstry{parsedFromFile.parse<0>(cstr);rapidxml::xml_node<>*addressNode=parsedFromFile.first_node("Address");outputAddress(*addressNode);// Print to stream using operator <<std::cout<<parsedFromFile;// Print to stream using print function, specifying printing flags// 0 means default printing flagsrapidxml::print(std::cout,parsedFromFile,0);}catch(constrapidxml::parse_error&e){std::cout<<"Parse error due to "<<e.what()<<std::endl;}// ---- create from scratch ----rapidxml::xml_node<>*addressNode=fromScratch.allocate_node(rapidxml::node_element,"Address");rapidxml::xml_node<>*recipientNode=fromScratch.allocate_node(rapidxml::node_element,"Recipient","Mr Malcolm Reynolds");rapidxml::xml_node<>*houseNode=fromScratch.allocate_node(rapidxml::node_element,"House","3");rapidxml::xml_node<>*streetNode=fromScratch.allocate_node(rapidxml::node_element,"Street","Serenity");rapidxml::xml_node<>*townNode=fromScratch.allocate_node(rapidxml::node_element,"Town","Space");rapidxml::xml_node<>*postCodeNode=fromScratch.allocate_node(rapidxml::node_element,"PostCode","DE18 5HI");rapidxml::xml_document<>fromScratch;addressNode->append_node(recipientNode);addressNode->append_node(houseNode);addressNode->append_node(streetNode);addressNode->append_node(townNode);addressNode->append_node(postCodeNode);fromScratch.append_node(addressNode);outputAddress(*addressNode);// ---- output to file ----std::ofstreamfout("test.xml");fout<<fromScratch;
// ---- parse from file ----TiXmlDocumentparsedFromFile("address.xml");parsedFromFile.LoadFile();outputAddress(parsedFromFile.FirstChildElement("Address")->ToElement());// ---- create from scratch ----TiXmlDocument*fromScratch=newTiXmlDocument();TiXmlElement*address=newTiXmlElement("Address");TiXmlElement*recipient=newTiXmlElement("Recipient");// LlinkEndChild causes memory managed object to be owned by the parent object// and destroyed by the parent when destroyedrecipient->LinkEndChild(newTiXmlText("Mr. Malcolm Reynolds"));address->LinkEndChild(recipient);TiXmlElement*house=newTiXmlElement("House");house->LinkEndChild(newTiXmlText("3"));address->LinkEndChild(house);TiXmlElement*street=newTiXmlElement("Street");street->LinkEndChild(newTiXmlText("Serenity"));address->LinkEndChild(street);TiXmlElement*town=newTiXmlElement("Town");town->LinkEndChild(newTiXmlText("Space"));address->LinkEndChild(town);TiXmlElement*postCode=newTiXmlElement("PostCode");postCode->LinkEndChild(newTiXmlText("DE18 5HI"));address->LinkEndChild(postCode);TiXmlElement*country=newTiXmlElement("Country");country->LinkEndChild(newTiXmlText("UK"));address->LinkEndChild(country);fromScratch->LinkEndChild(address);outputAddress(fromScratch->FirstChildElement("Address")->ToElement());// ---- parse from string ----std::stringstreamss;ss<<*fromScratch;TiXmlDocumentparsedFromString;parsedFromString.Parse(ss.str().c_str());outputAddress(parsedFromString.FirstChildElement("Address")->ToElement());// ---- output to file ----parsedFromString.SaveFile("test.xml");// destroying everything above that has been dynaimcally allocated by// destorying the root nodedeletefromScratch;
// ---- parse from file ----pugi::xml_documentparsedFromFile;pugi::xml_parse_resultresult=parsedFromFile.load_file("address.xml");if(result){outputAddress(parsedFromFile.child("Address"));}// ---- create from scratch ----pugi::xml_documentfromScratch;pugi::xml_nodeaddress=fromScratch.append_child("Address");address.append_child("Recipient").append_child(pugi::node_pcdata).set_value("Mr. Malcolm Reynolds");address.append_child("House").append_child(pugi::node_pcdata).set_value("3");address.append_child("Street").append_child(pugi::node_pcdata).set_value("Serenity");address.append_child("Town").append_child(pugi::node_pcdata).set_value("Space");address.append_child("PostCode").append_child(pugi::node_pcdata).set_value("DE18 5HI");address.append_child("County").append_child(pugi::node_pcdata).set_value("Solar System");address.append_child("Country").append_child(pugi::node_pcdata).set_value("UK");outputAddress(address);// ---- parse from stream ----// output document as xml to streamstd::stringstreamss;fromScratch.save(ss);pugi::xml_documentparsedFromStream;pugi::xml_parse_resultstreamResult=parsedFromStream.load(ss);if(streamResult){outputAddress(parsedFromStream.child("Address"));// ---- output to file ----std::ofstreamofs("test.xml");parsedFromStream.save(ofs);}
// ---- parse from file ----// every elelment is a ptreeboost::property_tree::ptreeparsedFromFile;try{// use trim whitespace to remove any whitespace// due to pretty formatting of xml structureread_xml("address.xml",parsedFromFile,boost::property_tree::xml_parser::trim_whitespace);outputAddress(parsedFromFile);}catch(constboost::property_tree::xml_parser::xml_parser_error&e){std::cout<<"Could not parse file due to "<<e.what()<<std::endl;}// ---- create from scratch ----boost::property_tree::ptreefromScratch;boost::property_tree::ptreeaddress;// replace put with add if element key is not guaranteed to be uniqueaddress.put("Recipient","Mr. Malcolm Reynolds");address.put("House","3");address.put("Street","Serenity");address.put("Town","Space");address.put("PostCode","DE18 5HI");address.put("County","Solar System");address.put("Country","UK");fromScratch.put_child("Address",address);outputAddress(fromScratch);// ---- parse from stream ----// write and read methods can take paths to filenames and streams as parametersstd::stringstreamss;write_xml(ss,fromScratch);boost::property_tree::ptreeparsedFromStream;read_xml(ss,parsedFromStream);outputAddress(parsedFromStream);// ---- output to file ----std::ofstreamfout("test.xml");write_xml(fout,parsedFromStream);
12345678910111213141516171819
voidoutputAddress(constboost::property_tree::ptree&pt){// can use dotted notation to specify element pathstd::stringstreamss;try{ss<<pt.get<std::string>("Address.Recipient")<<std::endl;ss<<pt.get<std::string>("Address.House")<<" "<<pt.get<std::string>("Address.Street")<<std::endl;ss<<pt.get<std::string>("Address.Town")<<std::endl;ss<<pt.get<std::string>("Address.PostCode")<<std::endl;ss<<pt.get<std::string>("Address.Country")<<std::endl;std::cout<<ss.str();}catch(constboost::property_tree::ptree_bad_path&e){std::cout<<e.what()<<std::endl;}}
Conclusion
I think the choice is very much dependent on what your use case is. If you have an xsd and it is small enough to allow free use of the xsd library (under the terms of their license) then I think that it is a very attractive option to consider.
Alternatively pugixml has a very simple api and is relatively fast, and so is an obvious choice, especially if there is a package available on your distribution that you can link against.
If you are looking for super fast encoding/decoding of xml then your two options are rapidxml and boost property tree. The boost library uses rapidxml to parse xml and therefore should be very comparable in speed. The good thing about boost is the fact that it allows a convenient package management solution for a header only library and boost is a very common requirement for a lot of projects, so in most cases you will already have access to the xml library, as well as slightly improving the rapidxml api.