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
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
:
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"
}
]
}
}
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.!
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):
Pros:
Cons:
For more details, for instance see
http://marko-editor.com/articles/property_tree_store_anything/
You can easily adapt this for variant usage.
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 .
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.
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.