在Qi JSON解析器中找不到丢失已解析属性的原因

时间:2013-01-22 15:29:29

标签: c++ boost boost-spirit boost-spirit-qi

问题

  1. 在Qi JSON解析器中找不到丢失已解析属性的原因。解析器成功解析输入字符串,但输出数据结构json_object仅包含第一个属性(attribute_a)但缺少其他属性(attribute_b和attribute_c)
  2. 软件:使用Boost 1.52提升Spirit Qi

    平台:Windows 7(64位)

    编译器(Visual Studio 2010)

    请求

    1. 帮助找出解析器找不到所有属性的原因。

    2. 查看调试输出,我看到属性没有被放入单个std :: vector对象中。我使用http://www.json.org/上的JSON语法作为参考。我希望看到' 成员的输出'是一个单独的std :: vector,包含为该JSON对象找到的所有json_pair对象的列表。

    3. 限制

      1. Parser不支持Unicode字符串。
      2. 代码:

            #define BOOST_SPIRIT_DEBUG
            #include <boost/spirit/include/qi.hpp>
            #include <boost/spirit/include/phoenix_core.hpp>
            #include <boost/spirit/include/phoenix_container.hpp>
            #include <boost/spirit/include/phoenix_statement.hpp>
            #include <boost/spirit/include/phoenix_operator.hpp>
            #include <boost/fusion/include/adapt_struct.hpp>
            #include <boost/fusion/include/boost_tuple.hpp>
            #include <boost/variant/recursive_variant.hpp>
        
            #include <boost/make_shared.hpp>
        
            #include <vector>
        
            namespace signal_processing {
              namespace parsing {
        
                struct json_object;
                struct json_array;
        
                typedef boost::variant < std::string,
                    double,
                    boost::recursive_wrapper<json_object>,
                    boost::recursive_wrapper<json_array>,
                    bool > json_value;
        
                typedef boost::tuple < std::string, json_value> json_pair;
        
                struct json_members
                {
                    std::vector < json_pair > items;
                };
        
                struct json_object
                {
                    std::vector < json_members > children;
                };
        
                struct json_array
                {
                    std::vector < json_value > list;
                };
        
                using boost::spirit::qi::bool_;
                using boost::spirit::qi::char_;
                using boost::spirit::qi::double_;
                using boost::spirit::qi::eol;
                using boost::spirit::qi::float_;
                using boost::spirit::qi::int_;
                using boost::spirit::qi::lexeme;
                using boost::spirit::qi::lit;   
                using boost::spirit::qi::space;
                using boost::spirit::qi::_val;
                using boost::spirit::qi::_1;
        
                template <typename Iterator, typename Skipper>
                struct json_grammar : boost::spirit::qi::grammar < Iterator, json_object(), Skipper>
                {
                    json_grammar() : json_grammar::base_type(object)
                    {
                        object = '{' > *members > '}';
        
                        pair = string > ':' > value;
        
                        members = pair > *( ',' > members );
        
                        element_list = '[' > *elements > ']';
        
                        elements = value > *( ',' > elements );
        
                        value = string |
                            number |
                            object |
                            element_list |
                            bool_ |
                            lit("null");
        
                        char const* exclude = " ();\"\n\r\t";
                        string = '"'
                            > +lexeme[char_ - char_(exclude)]
                            > '"';
        
                        // Return: double
                        number = double_ |
                            float_ |
                            int_;
        
                        BOOST_SPIRIT_DEBUG_NODE(object);
                        BOOST_SPIRIT_DEBUG_NODE(pair);
                        BOOST_SPIRIT_DEBUG_NODE(members);
                        BOOST_SPIRIT_DEBUG_NODE(element_list);
                        BOOST_SPIRIT_DEBUG_NODE(elements);
                        BOOST_SPIRIT_DEBUG_NODE(value);
                        BOOST_SPIRIT_DEBUG_NODE(string);
                        BOOST_SPIRIT_DEBUG_NODE(number);
                    }
        
                    boost::spirit::qi::rule < Iterator, json_object(), Skipper > object;
                    boost::spirit::qi::rule < Iterator, json_pair(), Skipper > pair;
                    boost::spirit::qi::rule < Iterator, json_members(), Skipper > members;
                    boost::spirit::qi::rule < Iterator, json_array(), Skipper > element_list;
                    boost::spirit::qi::rule < Iterator, json_array(), Skipper > elements;
                    boost::spirit::qi::rule < Iterator, json_value(), Skipper > value;
                    boost::spirit::qi::rule < Iterator, std::string(), Skipper > string;
                    boost::spirit::qi::rule < Iterator, double(), Skipper > number;
                };
              }
            }
        
            BOOST_FUSION_ADAPT_STRUCT(
                signal_processing::parsing::json_object,
                (std::vector < signal_processing::parsing::json_members >, children)
            )
        
            BOOST_FUSION_ADAPT_STRUCT(
                signal_processing::parsing::json_members,
                (std::vector < signal_processing::parsing::json_pair >, items)
            )
        
            BOOST_FUSION_ADAPT_STRUCT(
                signal_processing::parsing::json_array,
                (std::vector < signal_processing::parsing::json_value >, list)
            )
        
            void parse ( std::string const& file )
            {
                typedef signal_processing::parsing::json_grammar < std::string::const_iterator, boost::spirit::ascii::space_type > configuration_grammar;
                configuration_grammar input; // Input grammar
                signal_processing::parsing::json_object parsed_data;
        
                std::string::const_iterator iter = file.begin();
                std::string::const_iterator end = file.end();
                bool r = boost::spirit::qi::phrase_parse ( iter, end, input, boost::spirit::ascii::space, parsed_data );
        
                if ( ! r || iter != end)
                {
                    // Report the next 30 characters
                    std::string::const_iterator some = iter + 30;
        
                    if ( some > end )
                    {
                        some = end;
                    }
        
                    std::string context(iter, some);
                    std::cout << "-------------------------\n";
                    std::cout << "Parsing failed\n";
                    std::cout << "stopped at: \": " << context << "...\"\n";
                    std::cout << "-------------------------\n";
                }
            }
        
            int main(int,char**)
            {
                std::string input ( "{\r\n       \"Event\": {\r\n                \"attribute_a\": 0.0002,\r\n                \"attribute_b\": 2e-005,\r\n                \"attribute_c\": 0.022\r\n        }\r\n}" );
        
                parse ( input );
        
                return 0;
            }
        

2 个答案:

答案 0 :(得分:1)

解析器

总的来说,第一个问题的答案是,可能有几种方法可以达到你想要的效果。具体来说,我可以向您展示一种似乎有用的方式。

关键是正确设置匹配给定短语或标记的一个或多个实例的规则的属性类型,方法是确保您实际声明的是容器,而不是具有成员的结构是一个容器。

完成后,您需要编写规则,以便Qi知道您正在处理容器,并且应该在适当时回填。

首先查看成员规则:您提供此规则:

members = pair > *( ',' > members );

我不相信这本身就足以告诉Qi您希望它使用json_members来回填pair个容器,尤其是自pair s和{{ {1}}没有相同的属性类型。所以我建议你用以下代码替换规则:

member

甚至:

members = pair > *( ',' > pair );

你需要确定这些规则是否相同,但你明白了。

关于members = pair % ','; 类型:我更改了你的结构定义,使结构派生自容器,而不是将容器作为结构的属性:

json_members

你不需要对结构进行融合。

以下是我认为您的代码的工作版本:

struct json_members : std::vector < json_pair > {};

哪个输出:

#define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix_core.hpp>
    #include <boost/spirit/include/phoenix_container.hpp>
    #include <boost/spirit/include/phoenix_statement.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/include/boost_tuple.hpp>
    #include <boost/variant/recursive_variant.hpp>

    #include <boost/make_shared.hpp>

    #include <vector>

    namespace signal_processing {
      namespace parsing {

        struct json_object;
        struct json_array;

        typedef boost::variant < std::string,
            double,
            boost::recursive_wrapper<json_object>,
            boost::recursive_wrapper<json_array>,
            bool > json_value;

        typedef boost::tuple < std::string, json_value> json_pair;

//        struct json_members
//        {
//            std::vector < json_pair > items;
//        };
//
//        struct json_object
//        {
//            std::vector < json_members > children;
//        };
//
//        struct json_array
//        {
//            std::vector < json_value > list;
//        };

        struct json_members : std::vector < json_pair > {};
        struct json_object : std::vector < json_members > {};
        struct json_array : std::vector < json_value > {};

        using boost::spirit::qi::bool_;
        using boost::spirit::qi::char_;
        using boost::spirit::qi::double_;
        using boost::spirit::qi::eol;
        using boost::spirit::qi::float_;
        using boost::spirit::qi::int_;
        using boost::spirit::qi::lexeme;
        using boost::spirit::qi::lit;
        using boost::spirit::qi::space;
        using boost::spirit::qi::_val;
        using boost::spirit::qi::_1;

        template <typename Iterator, typename Skipper>
        struct json_grammar : boost::spirit::qi::grammar < Iterator, json_object(), Skipper>
        {
            json_grammar() : json_grammar::base_type(object)
            {
                object = '{' > *members > '}';

                pair = string > ':' > value;

                members = pair > *( ',' > pair );

                element_list = '[' > *elements > ']';

                elements = value > *( ',' > value );

                value = string |
                    number |
                    object |
                    element_list |
                    bool_ |
                    lit("null");

                char const* exclude = " ();\"\n\r\t";
                string = '"'
                    > +lexeme[char_ - char_(exclude)]
                    > '"';

                // Return: double
                number = double_ |
                    float_ |
                    int_;

                BOOST_SPIRIT_DEBUG_NODE(object);
                BOOST_SPIRIT_DEBUG_NODE(pair);
                BOOST_SPIRIT_DEBUG_NODE(members);
                BOOST_SPIRIT_DEBUG_NODE(element_list);
                BOOST_SPIRIT_DEBUG_NODE(elements);
                BOOST_SPIRIT_DEBUG_NODE(value);
                BOOST_SPIRIT_DEBUG_NODE(string);
                BOOST_SPIRIT_DEBUG_NODE(number);
            }

            boost::spirit::qi::rule < Iterator, json_object(), Skipper > object;
            boost::spirit::qi::rule < Iterator, json_pair(), Skipper > pair;
            boost::spirit::qi::rule < Iterator, json_members(), Skipper > members;
            boost::spirit::qi::rule < Iterator, json_array(), Skipper > element_list;
            boost::spirit::qi::rule < Iterator, json_array(), Skipper > elements;
            boost::spirit::qi::rule < Iterator, json_value(), Skipper > value;
            boost::spirit::qi::rule < Iterator, std::string(), Skipper > string;
            boost::spirit::qi::rule < Iterator, double(), Skipper > number;
        };
      }
    }

//    BOOST_FUSION_ADAPT_STRUCT(
//        signal_processing::parsing::json_object,
//        (std::vector < signal_processing::parsing::json_members >, children)
//    )
//
//    BOOST_FUSION_ADAPT_STRUCT(
//        signal_processing::parsing::json_members,
//        (std::vector < signal_processing::parsing::json_pair >, items)
//    )
//
//    BOOST_FUSION_ADAPT_STRUCT(
//        signal_processing::parsing::json_array,
//        (std::vector < signal_processing::parsing::json_value >, list)
//    )

    void parse ( std::string const& file )
    {
        typedef signal_processing::parsing::json_grammar < std::string::const_iterator, boost::spirit::ascii::space_type > configuration_grammar;
        configuration_grammar input; // Input grammar
        signal_processing::parsing::json_object parsed_data;

        std::string::const_iterator iter = file.begin();
        std::string::const_iterator end = file.end();
        bool r = boost::spirit::qi::phrase_parse ( iter, end, input, boost::spirit::ascii::space, parsed_data );

        if ( ! r || iter != end)
        {
            // Report the next 30 characters
            std::string::const_iterator some = iter + 30;

            if ( some > end )
            {
                some = end;
            }

            std::string context(iter, some);
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "stopped at: \": " << context << "...\"\n";
            std::cout << "-------------------------\n";
        }
    }

    int main(int,char**)
    {
        std::string input ( "{\r\n       \"Event\": {\r\n                \"attribute_a\": 0.0002,\r\n                \"attribute_b\": 2e-005,\r\n                \"attribute_c\": 0.022\r\n        }\r\n}" );

        parse ( input );

        return 0;
    }

<强>的Unicode

我知道boost :: spirit的SVN版本现在支持UTF8;尝试谷歌搜索localhost stov # ./stov <object> <try>{\r\n "Event": {</try> <members> <try>\r\n "Event": {\r</try> <pair> <try>\r\n "Event": {\r</try> <string> <try>\r\n "Event": {\r</try> <success>: {\r\n </success> <attributes>[[E, v, e, n, t]]</attributes> </string> <value> <try> {\r\n </try> <string> <try> {\r\n </try> <fail/> </string> <number> <try> {\r\n </try> <fail/> </number> <object> <try>{\r\n "</try> <members> <try>\r\n "a</try> <pair> <try>\r\n "a</try> <string> <try>\r\n "a</try> <success>: 0.0002,\r\n </success> <attributes>[[a, t, t, r, i, b, u, t, e, _, a]]</attributes> </string> <value> <try> 0.0002,\r\n </try> <string> <try> 0.0002,\r\n </try> <fail/> </string> <number> <try> 0.0002,\r\n </try> <success>,\r\n "</success> <attributes>[0.0002]</attributes> </number> <success>,\r\n "</success> <attributes>[0.0002]</attributes> </value> <success>,\r\n "</success> <attributes>[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002]]</attributes> </pair> <pair> <try>\r\n "a</try> <string> <try>\r\n "a</try> <success>: 2e-005,\r\n </success> <attributes>[[a, t, t, r, i, b, u, t, e, _, b]]</attributes> </string> <value> <try> 2e-005,\r\n </try> <string> <try> 2e-005,\r\n </try> <fail/> </string> <number> <try> 2e-005,\r\n </try> <success>,\r\n "</success> <attributes>[2e-05]</attributes> </number> <success>,\r\n "</success> <attributes>[2e-05]</attributes> </value> <success>,\r\n "</success> <attributes>[[[a, t, t, r, i, b, u, t, e, _, b], 2e-05]]</attributes> </pair> <pair> <try>\r\n "a</try> <string> <try>\r\n "a</try> <success>: 0.022\r\n }\r\n</success> <attributes>[[a, t, t, r, i, b, u, t, e, _, c]]</attributes> </string> <value> <try> 0.022\r\n }\r\n}</try> <string> <try> 0.022\r\n }\r\n}</try> <fail/> </string> <number> <try> 0.022\r\n }\r\n}</try> <success>\r\n }\r\n}</success> <attributes>[0.022]</attributes> </number> <success>\r\n }\r\n}</success> <attributes>[0.022]</attributes> </value> <success>\r\n }\r\n}</success> <attributes>[[[a, t, t, r, i, b, u, t, e, _, c], 0.022]]</attributes> </pair> <success>\r\n }\r\n}</success> <attributes>[[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002], [[a, t, t, r, i, b, u, t, e, _, b], 2e-05], [[a, t, t, r, i, b, u, t, e, _, c], 0.022]]]</attributes> </members> <members> <try>\r\n }\r\n}</try> <pair> <try>\r\n }\r\n}</try> <string> <try>\r\n }\r\n}</try> <fail/> </string> <fail/> </pair> <fail/> </members> <success>\r\n}</success> <attributes>[[[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002], [[a, t, t, r, i, b, u, t, e, _, b], 2e-05], [[a, t, t, r, i, b, u, t, e, _, c], 0.022]]]]</attributes> </object> <success>\r\n}</success> <attributes>[[[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002], [[a, t, t, r, i, b, u, t, e, _, b], 2e-05], [[a, t, t, r, i, b, u, t, e, _, c], 0.022]]]]</attributes> </value> <success>\r\n}</success> <attributes>[[[E, v, e, n, t], [[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002], [[a, t, t, r, i, b, u, t, e, _, b], 2e-05], [[a, t, t, r, i, b, u, t, e, _, c], 0.022]]]]]</attributes> </pair> <success>\r\n}</success> <attributes>[[[[E, v, e, n, t], [[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002], [[a, t, t, r, i, b, u, t, e, _, b], 2e-05], [[a, t, t, r, i, b, u, t, e, _, c], 0.022]]]]]]</attributes> </members> <members> <try>\r\n}</try> <pair> <try>\r\n}</try> <string> <try>\r\n}</try> <fail/> </string> <fail/> </pair> <fail/> </members> <success></success> <attributes>[[[[[E, v, e, n, t], [[[[a, t, t, r, i, b, u, t, e, _, a], 0.0002], [[a, t, t, r, i, b, u, t, e, _, b], 2e-05], [[a, t, t, r, i, b, u, t, e, _, c], 0.022]]]]]]]</attributes> </object> 。你现在可以使用库中的宽字符串支持来支持'unicode'(就像微软所说的那样)。

<强>声明

我在Linux上工作。 YMMV。

答案 1 :(得分:0)

我碰巧写了a UNICODE-aware JSON parser in Spirit v2 just recently,这是一个解析你样本的testcase

#include <sstream>
#include "JSON.hpp"

// util
static JSON::Value roundtrip(JSON::Value const& given) {
    return JSON::parse(to_wstring(given));
}

void roundtrip_test()
{
    auto 
        document = JSON::readFrom(std::istringstream(
                    "{\r\n"
                    "       \"Event\": {\r\n"
                    "             \"attribute_a\": 0.0002,\r\n"
                    "\"attribute_b\": 2e-005,\r\n"
                    "\"attribute_c\": 0.022\r\n"
                    "}\r\n}")),
        verify = roundtrip(document);

    std::cout << verify << "\n";
    std::cout << "document <=> verify equal:     \t" << std::boolalpha << (document == verify)                       << "\n";
    std::cout << "document <=> verify text match:\t" << std::boolalpha << (to_string(document) == to_string(verify)) << "\n";
}

打印:

{"Event":{"attribute_a":0.0002,"attribute_b":2e-05,"attribute_c":0.022}}
document <=> verify equal:      true
document <=> verify text match: true

更多API样本:

  1. “即时”JSON文档的对象初始化表达式:

    void initializer_test()
    {
        using namespace JSON;
    
        const Array arr { 
            L"text", 
            42,
            Object { { L"dummy", Null() } } 
        };
    
        auto radius = as_double(arr[1]);
    
        auto const document = Object {
                { L"number", 314e-2 },
                { L"string", L"hello\ngoodbye" },
                { L"array" , arr },
                { L"bool" , False() },
                { L"radius", radius },
                { L"area", radius * radius * 3.14 },
                { String { 10, L'=' }, String { 10, L'*' } }
        };
    
        std::cout << document[L"bool"]   << std::endl;
        std::cout << document[L"number"] << std::endl;
        std::cout << document[L"string"] << std::endl;
        std::cout << document[L"array"]  << std::endl;
        std::cout << document[L"bool"]   << std::endl;
        std::cout << document[L"radius"] << std::endl;
        std::cout << document[L"area"]   << std::endl;
        std::cout << document            << std::endl;
    }
    
  2. e.g。通过往返测试的this sample input

    {
        "Image": {
            "Width":  800,
            "Height": 600,
            "Title":  "View from 15th Floor",
            "Thumbnail": {
                "Russian":  "На берегу пустынных волн",
                "Escapes": "Ha \"\u0431\u0435\u0440\u0435\u0433\u0443\" shows up \\similar\\.\r\b\n",
                "берегу": "Russian",
                "Dummy": null,
                "Yummy": false,
                "Tummy": true,
                "Url":    "http://www.example.com/image/481989943",
                "Height": 125,
                "Width":  "100"
            },
            "IDs": [116, 943, 234, 38793]
    
        }
    }
    
  3. 转换某些JSON节点的访问者示例:How to manipulate leaves of a JSON tree