Search code examples
c++boostboost-spirit

Cannot add values to vector using boost spirit


I want to parse using the boost spirit the following string:

"{"DeliverNr":7424,"fruits":[["apple","banana","orange", "raspberry"]]}"

but I want to put into vector only three first fruits.

I have the following output:

it: { , dist: 0

Can anybody tell me what am I doing wrong ?

#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <vector>

struct SomeStructF
{
    std::string str1, str2, str3;
};

    using namespace boost::spirit;

    qi::rule<std::string::iterator> startRule;
    qi::rule<std::string::iterator> endRule;
    qi::rule<std::string::iterator, std::string()> stringRule;
    qi::rule<std::string::iterator, SomeStructF()> valueRule;
    qi::rule<std::string::iterator, std::vector<SomeStructF>()> valuesRule;
    char const QUOTATION_MARK{ '"' };
    char const OPEN_SQUARE_BRACKET{ '[' };

    startRule =
            +(qi::char_ - qi::char_(OPEN_SQUARE_BRACKET))
        >> qi::char_(OPEN_SQUARE_BRACKET);

    endRule = +qi::char_;

    stringRule =
            QUOTATION_MARK
        >> *(qi::char_ - QUOTATION_MARK)
        >> QUOTATION_MARK;

    valueRule =
            OPEN_SQUARE_BRACKET
        >> stringRule
        >> stringRule
        >> stringRule;

    valuesRule %=
            startRule
        >> valueRule
        >> endRule;

    std::vector<SomeStructF> res;
    auto it = data.begin();

    if (qi::parse(it, data.end(), valuesRule, res))
    {
        std::cout << "PARSE succeded" << std::endl;
    }
    std::cout << "it: " << *it << " , dist: " <<  std::distance(data.begin(), it) << std::endl;

    std::cout << "res size " << res.size() << std::endl;

BOOST_FUSION_ADAPT_STRUCT(
        parsers::SomeStructF,
        (std::string, str1)
        (std::string, str2)
        (std::string, str3)
)

Solution

  • It's not particularly clear to me what you are asking. It seems to me it's easy enough to take into acocunt the commas, as you noted:

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <iostream>
    #include <vector>
    #include <string>
    #include <iterator>
    #include <iomanip>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    
    namespace qi = boost::spirit::qi;
    
    struct SomeStructF {
        std::string str1, str2, str3;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(
            SomeStructF,
            (std::string, str1)
            (std::string, str2)
            (std::string, str3)
    )
    
    int main()
    {
        qi::rule<std::string::iterator> startRule;
        qi::rule<std::string::iterator> endRule;
        qi::rule<std::string::iterator, std::string()> stringRule;
        qi::rule<std::string::iterator, SomeStructF()> valueRule;
        qi::rule<std::string::iterator, SomeStructF()> valuesRule;
        char const QUOTATION_MARK{ '"' };
        char const OPEN_SQUARE_BRACKET{ '[' };
    
        startRule =
                +(qi::char_ - qi::char_(OPEN_SQUARE_BRACKET))
            >> qi::char_(OPEN_SQUARE_BRACKET);
    
        endRule = +qi::char_;
    
        stringRule =
                QUOTATION_MARK
            >> *(qi::char_ - QUOTATION_MARK)
            >> QUOTATION_MARK;
    
        valueRule =
                OPEN_SQUARE_BRACKET
            >> stringRule >> ','
            >> stringRule >> ','
            >> stringRule;
    
        valuesRule %=
                startRule
            >> valueRule
            >> endRule;
        BOOST_SPIRIT_DEBUG_NODES((startRule)(endRule)(stringRule)(valueRule)(valuesRule));
    
        std::string data = R"({"DeliverNr":7424,"fruits":
    [["apple","banana","orange","raspberry"]]})";
    
        std::vector<SomeStructF> res;
        auto it = data.begin();
    
        if (qi::parse(it, data.end(), valuesRule, res))
        {
            std::cout << "PARSE succeded" << std::endl;
            std::cout << "res size " << res.size() << std::endl;
    
            for (auto& el : res) {
                std::cout << "el: {" << std::quoted(el.str1) << ","
                                     << std::quoted(el.str2) << ","
                                     << std::quoted(el.str3) << "}\n";
            }
        }
        else
        {
            std::cout << "Parse did not succeed\n";
        }
    
        if (it != data.end())
            std::cout << "Remaining unparsed: '" << std::string(it, data.end()) << "'\n";
    
    }
    

    Prints

    PARSE succeded
    res size 1
    el: {"apple","banana","orange"}
    

    Your grammar (nor your question) shows how you'd like to detect multiple SomeStructF so I can't really know what you want. Perhaps you want to simplify:

    Live On Coliru

    using It = std::string::const_iterator;
    qi::rule<It, std::string()> quoted_ = '"' >> *~qi::char_('"') >> '"';
    qi::rule<It, SomeStructF()/*, qi::space_type*/> value_ = quoted_ >> ',' >> quoted_ >> ',' >> quoted_;
    using boost::spirit::repository::qi::seek;
    BOOST_SPIRIT_DEBUG_NODES((quoted_)(value_));
    
    std::string const data = R"({"DeliverNr":7424,"fruits":[["apple","banana","orange","raspberry"]]}
    {"DeliverNr":7435,"botanics":[["pine","maple","oak","pernambucco"]]})";
    
    std::vector<SomeStructF> res;
    It it = data.begin();
    
    if (qi::phrase_parse(it, data.end(), *seek["[[" >> value_], qi::space, res)) {
        for (auto& el : res) {
            std::cout << "el: {" << std::quoted(el.str1) << ","
                                 << std::quoted(el.str2) << ","
                                 << std::quoted(el.str3) << "}\n";
        }
    } else {
        std::cout << "Parse did not succeed\n";
    }
    

    Which prints

    el: {"apple","banana","orange"}
    el: {"pine","maple","oak"}
    Remaining unparsed: ',"pernambucco"]]}'