Why does Boost property tree write_json save everything as string? Is it possible to change that?

后端 未结 7 1195
忘掉有多难
忘掉有多难 2020-11-30 20:05

I\'m trying to serialize using boost property tree write_json, it saves everything as strings, it\'s not that data are wrong, but I need to cast them explicitly every time a

相关标签:
7条回答
  • 2020-11-30 20:19

    Ok, I've solved it like this, (of course it won't suite for everybody, as it is a bit of a hack, that need further work).


    I've wrote my own write_json function (simply copied the files, json_parser.hpp and json_parser_write.hpp to my project) and modified the following lines in json_parser_write.hpp:

    1. commented line 37 - escaping the quote '"'
    2. changed line 76 - so that it doesn't add quotes anymore: stream << Ch('"') << data << Ch('"'); ==> stream << data;

    Then values will be saved properly except for strings, so I wrote custom translator for it:

    template <typename T>
    struct my_id_translator
    {
        typedef T internal_type;
        typedef T external_type;
    
        boost::optional<T> get_value(const T &v) { return  v.substr(1, v.size() - 2) ; }
        boost::optional<T> put_value(const T &v) { return '"' + v +'"'; }
    };
    

    and simply saved string using:

    elem2.put<std::string>("key2", "asdf", my_id_translator<std::string>());
    

    complete program:

    #include <iostream>
    #include <string>
    #include <sstream>
    
    #include <boost/property_tree/ptree.hpp>
    
    #include "property_tree/json_parser.hpp" // copied the headers
    
    template <typename T>
    
    struct my_id_translator
    {
        typedef T internal_type;
        typedef T external_type;
    
        boost::optional<T> get_value(const T &v) { return  v.substr(1, v.size() - 2) ; }
        boost::optional<T> put_value(const T &v) { return '"' + v +'"'; }
    };
    
    int main(int, char *[])
    {
        using namespace std;
        using boost::property_tree::ptree;
        using boost::property_tree::basic_ptree;
        try
        {
            ptree root, arr,elem2;
            basic_ptree<std::string, std::string> elem1;
            elem1.put<int>("int", 10 );
            elem1.put<bool>("bool", true);
            elem2.put<double>("double", 2.2);
            elem2.put<std::string>("string", "some string", my_id_translator<std::string>());
    
            arr.push_back( std::make_pair("", elem1) );
            arr.push_back( std::make_pair("", elem2) );
            root.put_child("path1.path2", arr);
    
            std::stringstream ss;
            write_json(ss, root);
            std::string my_string_to_send_somewhere_else = ss.str();
    
            cout << my_string_to_send_somewhere_else << endl;
    
        }
        catch (std::exception & e)
        {
            cout << e.what();
        }
        return 0;
    }
    

    result :)

    {
        "path1":
        {
            "path2":
            [
                {
                    "int": 10,
                    "bool": true
                },
                {
                    "double": 2.2,
                    "string": "some string"
                }
            ]
        }
    }
    
    0 讨论(0)
  • 2020-11-30 20:24

    Boost confirms its implementation has no 100% conformance to JSON standard. Check the following link to see their explanation: Making a ptree variant that preserves JSON types is a future plan, but far off.!

    0 讨论(0)
  • 2020-11-30 20:25

    All solutions, that require custom translators for strings explicitly, seem to be quite error prone for me since it's likely to forget it sometimes. It would be nice to have some kind of overload way via inheritance for the property tree's put method to handle this implicitly but that's not possible in a robust way since it's a template and you would have to ensure full covariance for all methods of the tree. Also changing boost library stuff as a workaround should be avoided in general if possible.

    The most robust way without hacks I found so far is (since C++11):

    • use a boost-property tree with <KeyType, std::variant< yourTypes> >
    • Provide a translator for your variant (details see link below!) but do not "hackify" the content in terms of JSON-specifics! These are almost totally orthogonal aspects!
    • Write an own reader and writer for JSON, should be easily adapted from the Boost ones

    Pros:

    • no hacks required to affect the property tree in terms of JSON-specific details
    • no pollution of the Boost libraries and their namespaces except the specializations of your new own types (the variant translator)
    • type safer than the custom string based property tree approach
    • should be faster for many runtime scenarios with non-frequent serialization of the tree

    Cons:

    • requires some efforts for a quite small detail of the behavior
    • might be a bit slower in terms of compilation
    • might be a bit slower for runtime scenarios with frequent serialization and minor changes of the tree (can be optimized for sure)
    • reading the json back into the tree is some kind of philosophic work in doubt to ensure as much symmetry as possible between the used types (for many purposes rather an academic issue)

    For more details, for instance see

    http://marko-editor.com/articles/property_tree_store_anything/

    You can easily adapt this for variant usage.

    0 讨论(0)
  • 2020-11-30 20:28

    From the outputted JSON it is clear that the serializer serializes everything to strings using some sort of .toString() method - that is, its unaware of the type of each member and so encloses everything in " ".

    See Creating JSON arrays in Boost using Property Trees for more about this problem .

    0 讨论(0)
  • 2020-11-30 20:32

    As we have typedef basic_ptree<std::string, std::string> ptree; in the boost libraries, boost will always serialize each value as string and parse all values to a string equivalent.

    0 讨论(0)
  • 2020-11-30 20:34

    I ended up adding another function to my utils to solve this:

    #include <string>
    #include <regex>
    #include <boost/property_tree/json_parser.hpp>
    
    namespace bpt = boost::property_tree;
    typedef bpt::ptree JSON;
    namespace boost { namespace property_tree {
        inline void write_jsonEx(const std::string & path, const JSON & ptree)
        {
            std::ostringstream oss;
            bpt::write_json(oss, ptree);
            std::regex reg("\\\"([0-9]+\\.{0,1}[0-9]*)\\\"");
            std::string result = std::regex_replace(oss.str(), reg, "$1");
    
            std::ofstream file;
            file.open(path);
            file << result;
            file.close();
        }
    } }
    

    Hope that helps.

    0 讨论(0)
提交回复
热议问题