Serializing and deserializing JSON with Boost
Note that property_tree
interprets the keys as paths, e.g. putting the pair "a.b"="z" will create an {"a":{"b":"z"}} JSON, not an {"a.b":"z"}. Otherwise, using property_tree
is trivial. Here is a little example.
#include <sstream>
#include <map>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
void example() {
// Write json.
ptree pt;
pt.put ("foo", "bar");
std::ostringstream buf;
write_json (buf, pt, false);
std::string json = buf.str(); // {"foo":"bar"}
// Read json.
ptree pt2;
std::istringstream is (json);
read_json (is, pt2);
std::string foo = pt2.get<std::string> ("foo");
}
std::string map2json (const std::map<std::string, std::string>& map) {
ptree pt;
for (auto& entry: map)
pt.put (entry.first, entry.second);
std::ostringstream buf;
write_json (buf, pt, false);
return buf.str();
}
Boost serialize object as a json
No there's not such a thing.
You could write your own (by implementing the Archive concept). But I reckon that's not worth the effort. Just use a JSON library.
Here's a sketch of the minimal output-archive model that works with your sample:
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <iostream>
#include <iomanip>
#include <sstream>
struct MyOArchive {
std::ostream& _os;
MyOArchive(std::ostream& os) : _os(os) {}
using is_saving = boost::true_type;
template <typename T>
MyOArchive& operator<<(boost::serialization::nvp<T> const& wrap) {
save(wrap.name(), wrap.value());
return *this;
}
template <typename T>
MyOArchive& operator<<(T const& value) {
return operator<<(const_cast<T&>(value));
}
template <typename T>
MyOArchive& operator<<(T& value) {
save(value);
return *this;
}
template <typename T> MyOArchive& operator&(T const& v) { return operator<<(v); }
bool first_element = true;
void start_property(char const* name) {
if (!first_element) _os << ", ";
first_element = false;
_os << std::quoted(name) << ":";
}
template <typename T> void save(char const* name, T& b) {
start_property(name);
save(b);
}
void save(bool b) { _os << std::boolalpha << b; }
void save(int i) { _os << i; }
void save(std::string& s) { _os << std::quoted(s); }
template <typename T>
void save(T& v) {
using boost::serialization::serialize;
_os << "{";
first_element = true;
serialize(*this, v, 0u);
_os << "}\n";
first_element = false;
}
};
class Animal {
public:
Animal() {}
void set_leg(int l) { legs = l; };
void set_name(std::string s) { name = s; };
void set_ismammal(bool b) { is_mammal = b; };
void print();
private:
friend class boost::serialization::access;
template <typename Archive> void serialize(Archive &ar, unsigned)
{
ar & BOOST_SERIALIZATION_NVP(legs)
& BOOST_SERIALIZATION_NVP(is_mammal)
& BOOST_SERIALIZATION_NVP(name);
}
int legs;
bool is_mammal;
std::string name;
};
void Animal::print() {
std::cout << name << " with " << legs << " legs is " << (is_mammal ? "" : "not ") << "a mammal" << std::endl;
}
void save_obj(const Animal &animal, std::stringstream &stream) {
MyOArchive oa{ stream };
oa << animal;
}
int main() {
std::stringstream stream;
{
Animal animal;
animal.set_name("Horse");
animal.set_leg(4);
animal.set_ismammal(true);
save_obj(animal, stream);
}
std::cout << "stream print: " << stream.str() << std::endl;
}
Prints
stream print: {"legs":4, "is_mammal":true, "name":"Horse"}
CAVEAT
I do not recommend this approach. In fact there are numerous missing things in the above - most notably the fact that it is output-only
Boost JSON serialization format (boost 1.76.0)
To the best of my knowledge that is not a feature. There has been a fair bit of discussion on the boost mailing list when the library was being reviewed prior to acceptance, so if you want you can check the archives for the rationale.
My recollection of it is that the library focuses on a narrow featureset facilitating machine-to-machine transport (99% of JSON, e.g. in restful APIs). That implies a focus on
- making it correct
- making it fast
The same thing came up a day or so ago: Is there a way to switch boost::json::serializer to beautified output? (where I quote from the documentation intro section)
Is there a way to switch boost::json::serializer to beautified output?
This is not a feature of the library. The library has goals stated in the intro. It doesn't include user-friendly presentation formatting. In fact, even their number formatting can be said to be down-right user hostile.
The promise of the library centers at data interchange between computer systems:
This library focuses on a common and popular use-case: parsing and serializing to and from a container called value which holds JSON types. Any value which you build can be serialized and then deserialized, guaranteeing that the result will be equal to the original value. Whatever JSON output you produce with this library will be readable by most common JSON implementations in any language.
There's an example that shows how to do simple pretty printing if you want to avoid a large part of the work/have a reasonable starting point to not forget too many things: https://www.boost.org/doc/libs/1_76_0/libs/json/doc/html/json/examples.html
Alternatively, you can use any of the existing JSON libraries with a richer feature set. Keep in mind they will make difference trade offs, so you may run into other limitations.
Serializing std::list into json with boost ptree
Paths in your tree are always strings. The compiler will tell you this in the remainder of the message. Arguably the documentation is a more readable source:
Both
key_type
anddata_type
are configurable, but will usually bestd::string
here
The
self_type & put(const path_type & path, const Type & value, Translator tr);
So the essence of the fix is
pt.put(std::to_string(entry.id), entry.code);
I got a little bit side-tracked cleaning up the code, so here goes:
Self Contained Sample
// FILE: some header
#include <ostream>
struct SiteCode {
int id;
int code;
SiteCode(int id, int code) : id(id), code(code)
{ }
friend inline std::ostream &operator<<(std::ostream &out, SiteCode const& site) {
return out << "(" << site.id << "," << site.code << ")";
}
};
#include <list> // I have deleted some header for sake of readability
// FILE: sqliteDB header
class sqliteDB {
using Records = std::list<SiteCode>;
Records _records;
public:
void load();
Records const& get() const { return _records; }
void printList() const;
void writeJson(std::ostream& os) const;
};
// FILE: some sqlpp.hpp utility header (inline implementations only)
#include <memory>
#include <sqlite3.h>
namespace sqlpp {
using database = std::shared_ptr<::sqlite3>;
void perror(int rc) {
if (rc != SQLITE_OK) throw std::runtime_error(::sqlite3_errstr(rc));
}
struct statement {
static statement prepare(database db, std::string const& sql) {
::sqlite3_stmt* stmt = nullptr;
perror(::sqlite3_prepare_v2(db.get(), sql.c_str(), -1, &stmt, 0));
return { handle(stmt, ::sqlite3_finalize), db };
}
int step() { return ::sqlite3_step(_stmt.get()); }
int column_int(int c) { return ::sqlite3_column_int(_stmt.get(), c); }
private:
using handle = std::shared_ptr<::sqlite3_stmt>;
database _db; // keeping it around for the lifetime of _stmt
handle _stmt;
statement(handle&& h, database& db) : _db(db), _stmt(std::move(h)) { }
};
database open(char const* path) {
::sqlite3* db = nullptr;
perror(::sqlite3_open(path, &db));
return database(db, ::sqlite3_close);
}
statement prepare(database db, std::string const& sql) {
return statement::prepare(db, sql);
}
}
// FILE: sqliteDB implementation file
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
void sqliteDB::load() {
using namespace sqlpp;
auto stmt = prepare(open("/tmp/database.db"), "SELECT ID, CODE FROM SiteCode;");
while (stmt.step() == SQLITE_ROW)
_records.emplace_back(stmt.column_int(0), stmt.column_int(1));
}
void sqliteDB::writeJson(std::ostream& os) const {
using namespace boost::property_tree;
ptree pt;
for (auto &entry : _records)
pt.put(std::to_string(entry.id), entry.code);
write_json(os, pt, false);
}
// FILE: main program
template <typename List>
static void printList(List const& list) {
int s = list.size();
std::cout << "The number of Records is: " << s << "\n";
for (auto& r : list) std::cout << r << " ";
}
void dump(sqliteDB const& db) {
printList(db.get());
std::cout << "\n==============[ AS JSON ]===============\n";
db.writeJson(std::cout);
}
int main() {
sqliteDB db;
std::cout << "before loading: \n";
dump(db);
std::cout << "after loading: \n";
db.load();
dump(db);
}
Just compile as g++ -std=c++11 -g -Wall -Wextra -pedantic main.cpp -lsqlite3
and get:
sehe@desktop:/tmp$ sqlite3 database.db <<< "create table SiteCode (id int primary key, code int);"
sehe@desktop:/tmp$ for a in {1..10}; do echo "insert into SiteCode(ID,CODE) VALUES($a, $RANDOM);"; done | sqlite3 database.db
sehe@desktop:/tmp$ ./test
Output
before loading:
The number of Records is: 0
==============[ AS JSON ]===============
{}
after loading:
The number of Records is: 10
(1,5591) (2,31578) (3,30641) (4,4850) (5,1628) (6,5133) (7,8798) (8,20601) (9,21213) (10,18222)
==============[ AS JSON ]===============
{"1":"5591","2":"31578","3":"30641","4":"4850","5":"1628","6":"5133","7":"8798","8":"20601","9":"21213","10":"18222"}
C++ JSON Serialization
For that you need reflection in C/C++, which doesn't exist. You need to have some meta data describing the structure of your classes (members, inherited base classes). For the moment C/C++ compilers don't automatically provide that information in built binaries.
I had the same idea in mind, and I used GCC XML project to get this information. It outputs XML data describing class structures.
I have built a project and I'm explaining some key points in this page :
Serialization is easy, but we have to deal with complex data structure implementations (std::string, std::map for example) that play with allocated buffers.
Deserialization is more complex and you need to rebuild your object with all its members, plus references to vtables ... a painful implementation.
For example you can serialize like this:
// Random class initialization
com::class1* aObject = new com::class1();
for (int i=0; i<10; i++){
aObject->setData(i,i);
}
aObject->pdata = new char[7];
for (int i=0; i<7; i++){
aObject->pdata[i] = 7-i;
}
// dictionary initialization
cjson::dictionary aDict("./data/dictionary.xml");
// json transformation
std::string aJson = aDict.toJson<com::class1>(aObject);
// print encoded class
cout << aJson << std::endl ;
To deserialize data it works like this:
// decode the object
com::class1* aDecodedObject = aDict.fromJson<com::class1>(aJson);
// modify data
aDecodedObject->setData(4,22);
// json transformation
aJson = aDict.toJson<com::class1>(aDecodedObject);
// print encoded class
cout << aJson << std::endl ;
Ouptuts:
>:~/cjson$ ./main
{"_index":54,"_inner": {"_ident":"test","pi":3.141593},"_name":"first","com::class0::_type":"type","com::class0::data":[0,1,2,3,4,5,6,7,8,9],"com::classb::_ref":"ref","com::classm1::_type":"typem1","com::classm1::pdata":[7,6,5,4,3,2,1]}
{"_index":54,"_inner":{"_ident":"test","pi":3.141593},"_name":"first","com::class0::_type":"type","com::class0::data":[0,1,2,3,22,5,6,7,8,9],"com::classb::_ref":"ref","com::classm1::_type":"typem1","com::classm1::pdata":[7,6,5,4,3,2,1]}
>:~/cjson$
Usually these implementations are compiler dependent (ABI Specification for example), and require external descriptions to work (GCCXML output), thus are not really easy to integrate to projects.
Related Topics
Recommended Values for Opencv Detectmultiscale() Parameters
Is There a Name for This Tuple-Creation Idiom
Why Is Cuda Pinned Memory So Fast
Clean C++ Granular Friend Equivalent? (Answer: Attorney-Client Idiom)
Why Using the Const Keyword Before and After Method or Function Name
Difference Between Std::Result_Of and Decltype
Which Stl Container Should I Use for a Fifo
How to Solve Memory Fragmentation
Std::Thread Calling Method of Class
Differencebetween a Template Class and a Class Template
Generating a Normal Map from a Height Map
Differencebetween a Static and Const Variable
How to Get a "Codesigned" Gdb on Osx
How to Create Unique_Ptr That Holds an Allocated Array
What Is the C++ Function to Raise a Number to a Power
How to Avoid Code Duplication Implementing Const and Non-Const Iterators