[Solved] c++ How to read XML using boost xml parser and store in map

As an example I am using the XML file listed here:

https://msdn.microsoft.com/en-us/library/ms256129(v=vs.110).aspx

The XML File:

<?xml version="1.0"?>
<purchaseOrder xmlns="http://tempuri.org/po.xsd" orderDate="1999-10-20">
    <shipTo country="US">
        <name>Alice Smith</name>
        <street>123 Maple Street</street>
        <city>Mill Valley</city>
        <state>CA</state>
        <zip>90952</zip>
    </shipTo>
    <billTo country="US">
        <name>Robert Smith</name>
        <street>8 Oak Avenue</street>
        <city>Old Town</city>
        <state>PA</state>
        <zip>95819</zip>
    </billTo>
    <comment>Hurry, my lawn is going wild!</comment>
    <items>
        <item partNum="872-AA">
            <productName>Lawnmower</productName>
            <quantity>1</quantity>
            <Price>
                <USPrice>148.95</USPrice>
                <UKPrice>150.02</UKPrice>
            </Price>
            <comment>Confirm this is electric</comment>
        </item>
        <item partNum="926-AA">
            <productName>Baby Monitor</productName>
            <quantity>1</quantity>
            <Price>
                <USPrice>39.95</USPrice>
                <UKPrice>37.67</UKPrice>
            </Price>
            <USPrice>39.98</USPrice>
            <shipDate>1999-05-21</shipDate>
        </item>
    </items>
</purchaseOrder>

Currently I am using the following code but using that I am able to read only one child node that is purchaseOrder.shipTo country only. how to read till tag USPrice? Does boost xml parser support 4 level tag value parsing?

const std::string XML_PATH1 = "./test1.xml";
#define ROOTTAG  "purchaseOrder"
    boost::property_tree::ptree pt;
    boost::property_tree::read_xml( XML_PATH1, pt);
    BOOST_FOREACH(boost::property_tree::ptree::value_type & v, pt.get_child(ROOTTAG)){
        xmlmap[v.first.data()] = v.second.data();
    }

I want to read and store as follows in the xmlmap <string, string>.

map key = items.item partNum.USPrice
map value = 39.98 (post converting to string)

Update:

I tried the following but it is giving me compilation error as

error: ‘boost::property_tree::ptree’ has no member named ‘second’
                         boost::property_tree::ptree lt = subtree.second;

Code:

const std::string XML_PATH1 = "./test1.xml";
#define ROOTTAG  "purchaseOrder"
boost::property_tree::ptree pt1;
boost::property_tree::read_xml( XML_PATH1, pt1);
BOOST_FOREACH(boost::property_tree::ptree::value_type & node, pt1.get_child(ROOTTAG))
{
    std::string tagname = node.first;
    tagname += ".";
    boost::property_tree::ptree subtree = node.second;
    BOOST_FOREACH( boost::property_tree::ptree::value_type & v, subtree.get_child(node.first.data()))
    {
        boost::property_tree::ptree lt = subtree.second;
        tagname += v.first.data();
        tagname += ".";
        BOOST_FOREACH( boost::property_tree::ptree::value_type & vt, lt.get_child(v.first.data()))
            {
                std::string name1 = vt.first.data();
                tagname += name1;
                if(name1 != "<xmlattr>") {
                    std::string tagvalue = lt.get<std::string>(name1);
                    tagname += name1;
                    xmlmap[tagname] = tagvalue;
                    }
             }
      }
}
Enquirer: Namitha

||

Solution #1:

It seems like a really useless thing to do.

A property tree is /already/ that “structured map”:

auto po = pt.get_child("purchaseOrder");
std::cout << "items.item.Price.USPrice: '" << po.get("items.item.Price.USPrice", "") << "'n";

If you want XPath, use an XPath capable XML library (What XML parser should I use in C++?).

If you want easier access, write some translators or access functions. But whatever you do, you probably do not wish to through the structural information away, as your question kind of suggests.

Here’s a sample of something I’d find useful:

Live On Coliru

#include <boost/property_tree/xml_parser.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iostream>
#include <map>

using Decimal = boost::multiprecision::cpp_dec_float_50;
using Tree    = boost::property_tree::ptree;

namespace BackOffice {
    struct purchaseOrder {
        struct address {
            std::string country, name, street, city, state, zip;
        } shipTo, billTo;

        std::string comment;

        struct item {
            std::string partNum, productName, comment;
            size_t quantity;

            struct Price { 
                Decimal USPrice, UKPrice;
            } price;
        };

        std::vector<item> items;
    };

    void read_tree(Tree const& tree, purchaseOrder::address& into);
    void read_tree(Tree const& tree, purchaseOrder::item::Price& into);
    void read_tree(Tree const& tree, purchaseOrder::item& into);
    void read_tree(Tree const& tree, purchaseOrder& into);

    template <typename T, typename Indirect>
    void read_tree(Indirect const& maybeTree, T& into, decltype(&*maybeTree) = nullptr) {
        if (maybeTree) read_tree(*maybeTree, into); else into = {};
    }

    template <typename T, typename Indirect>
    void read_tree(Indirect const& maybeTree, std::string sub, T& into, decltype(&*maybeTree) = nullptr) {
        if (maybeTree) read_tree(*maybeTree, sub, into); else into = {};
    }


    template <typename T>
    void read_tree(Tree const& tree, std::string sub, std::vector<T>& into) {
        for (auto& child : tree) {
            if (child.first == sub) {
                into.emplace_back();
                read_tree(child.second, into.back());
            }
        }
    }

    void read_tree(Tree const& tree, purchaseOrder::address& into) {
        into.country = tree.get("<xmlattr>.country", "(unknown");
        into.name    = tree.get("name",              "(unknown");
        into.street  = tree.get("street",            "(unknown");
        into.city    = tree.get("city",              "(unknown");
        into.state   = tree.get("state",             "(unknown");
        into.zip     = tree.get("zip",               "(unknown");
    }

    void read_tree(Tree const& tree, purchaseOrder::item::Price& into) {
        into.UKPrice = tree.get("UKPrice", Decimal{});
        into.USPrice = tree.get("USPrice", Decimal{});
    }

    void read_tree(Tree const& tree, purchaseOrder::item& into) {
        into.partNum     = tree.get("<xmlattr>.partNum", "(unknown");
        into.productName = tree.get("productName",       "(unknown");
        into.comment     = tree.get("comment",           "");
        read_tree(tree.get_child_optional("Price"), into.price);
    }

    void read_tree(Tree const& tree, purchaseOrder& into) {
        read_tree(tree.get_child_optional("shipTo"), into.shipTo);
        read_tree(tree.get_child_optional("billTo"), into.billTo);
        read_tree(tree.get_child_optional("items"), "item", into.items);
        into.comment = tree.get("comment", "");
    }

}

int main() {
    Tree pt;
    read_xml( "input.txt", pt);

    //auto po = pt.get_child("purchaseOrder");
    //std::cout << "items.item.Price.USPrice: '" << po.get("items.item.Price.USPrice", "") << "'n";

    BackOffice::purchaseOrder po;
    read_tree(pt.get_child("purchaseOrder"), po);
}
Respondent: Namitha

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published.