Search code examples
c++parsingboostboost-spiritboost-spirit-qi

Parsing comma separated grammars when unordered


From a previous post I found a way to parse with boost::spirit a struct of this type:

"parameter" : {
  "name"        : "MyName"       , 
  "type"        : "MyType"       , 
  "unit"        : "MyUnit"       , 
  "cardinality" : "MyCardinality", 
  "value"       : "MyValue"       
}

It's a simple JSON with key-value pairs. Now I want to parse this struct regardless to variable orders. I.e. I want to parse into the same object also this struct:

"parameter" : {
  "type"        : "MyType"       ,
  "value"       : "MyValue"      ,
  "unit"        : "MyUnit"       , 
  "cardinality" : "MyCardinality",
  "name"        : "MyName"
}

I know that I can use the ^ operator in order to parse data in any order but I dont't know how to handles commas at ends of lines but last. How can I parse both structures?

This is the @sehe code from previous post. Grammar is defined here.

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

// This is pasted and copied from another header file

namespace StateMachine {
namespace Private {

    struct LuaParameterData {
        std::wstring name;
        std::wstring type;
        std::wstring unit;
        std::wstring cardinality;
        std::wstring value;
    };

} // namespace Private
} // namespace StateMachine

BOOST_FUSION_ADAPT_STRUCT(
  StateMachine::Private::LuaParameterData,
  (std::wstring, name)
  (std::wstring, type)
  (std::wstring, unit)
  (std::wstring, cardinality)
  (std::wstring, value)
)

namespace qi = boost::spirit::qi;

// From here original file continues
namespace StateMachine {
namespace Private {

    template<typename Iterator>
    struct LuaParameterDataParser : qi::grammar<Iterator, LuaParameterData(), qi::ascii::space_type>
    {
        LuaParameterDataParser() : LuaParameterDataParser::base_type(start)
        {
            quotedString = qi::lexeme['"' >> +(qi::ascii::char_ - '"') >> '"'];

            start =
                qi::lit("\"parameter\"")
                >> ':'
                >> '{'
                >> qi::lit("\"name\""       ) >> ':' >> quotedString >> ','
                >> qi::lit("\"type\""       ) >> ':' >> quotedString >> ','
                >> qi::lit("\"unit\""       ) >> ':' >> quotedString >> ','
                >> qi::lit("\"cardinality\"") >> ':' >> quotedString >> ','
                >> qi::lit("\"value\""      ) >> ':' >> quotedString
                >> '}'
                ;

            BOOST_SPIRIT_DEBUG_NODES((start)(quotedString));
        }

        qi::rule<Iterator, std::string(), qi::ascii::space_type> quotedString;
        qi::rule<Iterator, LuaParameterData(), qi::ascii::space_type> start;
    };

} // namespace Private
} // namespace StateMachine

int main() {
    using It = std::string::const_iterator;

    std::string const input = R"(
        "parameter" : {
            "name"        : "name"       , 
            "type"        : "type"       , 
            "unit"        : "unit"       , 
            "cardinality" : "cardinality", 
            "value"       : "value"       
        }
    )";
    It f = input.begin(), 
       l = input.end();

    StateMachine::Private::LuaParameterDataParser<It> p;
    StateMachine::Private::LuaParameterData data;
    bool ok = qi::phrase_parse(f, l, p, qi::ascii::space, data);

    if (ok) {
        std::wcout << L"Parsed: \n";
        std::wcout << L"\tname: " << data.name << L'\n';
        std::wcout << L"\ttype: " << data.type << L'\n';
        std::wcout << L"\tunit: " << data.unit << L'\n';
        std::wcout << L"\tcardinality: " << data.cardinality << L'\n';
        std::wcout << L"\tvalue: " << data.value << L'\n';
    } else {
        std::wcout << L"Parse failure\n";
    }

    if (f!=l)
        std::wcout << L"Remaining unparsed: '" << std::wstring(f,l) << L"'\n";
}

Solution

  • I'm going to refer to a set of recent answers where I've been over things quite extensively:

    Tangentially related: