Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
936 views
in Technique[技术] by (71.8m points)

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;
                    }
             }
      }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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", "") << "'
";

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", "") << "'
";

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...