Search code examples
c++jsonserializationboostboost-propertytree

Serializing std::list into json with boost ptree


okay, so I am banging my head on this for last couple of days but still I am unable to get it right. I have a std::list container and I want to serialize it into JSON string so that I can send it over network.

NOTE: I compile my code using following:

g++ -std=c++11 -o main main.cpp DBAccess11.cpp -lsqlite3 -lboost_serialization

I took help of this and this

Below is my DBAccess1.h file.

#ifndef DBAccess1_HH  
#define DBAccess1_HH

#include <list> // I have deleted some header for sake of readability
#include <boost/serialization/list.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

using namespace std;
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
using boost::property_tree::basic_ptree;

//================================//   
struct SiteCode
{
      int siteID;
      int siteCode;
};

inline ostream& operator<< (ostream &out, SiteCode &site)
{
    out << "(" << site.siteID << "," << site.siteCode << ")";
    return out;
}
//================================//

class sqliteDB {
    list<SiteCode> Site_Code_list;
public:
    list<SiteCode> GET_ALL_Site_Code();
    void printList();
};
#endif**

Below is the DBAccess11.cpp file where all the functions are defined

#include <list> // I have deleted some header for sake of readability
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/serialization/list.hpp>
#include "DBAccess1.h"

using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
using boost::property_tree::basic_ptree;

list<SiteCode> sqliteDB::GET_ALL_Site_Code()
{
        sqlite3 *db;
        const char *sql;
        sqlite3_stmt * stmt;

        int rc = sqlite3_open("/path/to/database.db", &db);
        sql = "SELECT * FROM SiteCode;";       
        rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);

    while(sqlite3_step(stmt)==SQLITE_ROW) {

            int A  = sqlite3_column_int(stmt, 0);
            int B = sqlite3_column_int(stmt, 1);

            SiteCode info;
            info.siteID = A;
            info.siteCode = B;              

            cout<<"Preparing to push data into List"<<endl;
            Site_Code_list.push_back(info);
            cout<<"Data was pushed successfully"<<endl;

            ptree pt;
            for (auto& entry: list<SiteCode> Site_Code_list)  //<< ERROR LINE80
            pt.put(entry.siteID, entry.siteCode);
            std::ostringstream buf; 
            write_json (buf, pt, false); 
            cout<< buf.str() << endl;
    }

    sqlite3_finalize(stmt);
    sqlite3_close(db);
    return Site_Code_list;
}
//====================================================//
void sqliteDB::printList()
{
     int s = Site_Code_list.size();
     cout << "The size of List is :" << s << endl;
     for( list<SiteCode> :: iterator it = Site_Code_list.begin(); it !=  Site_Code_list.end(); it++)     
     cout << *it << " ";
}

Below is main.cpp

#include <list> // I have deleted some header for sake of readability
#include <boost/serialization/list.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

using namespace std;
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
using boost::property_tree::basic_ptree;

int main()
{
    sqliteDB object1;
    object1.GET_ALL_Site_Code();
    object1.printList();
    cout << "\n\nAll the statement were executed properly\n\n";
    return 0;
}

The error I get is as follows:

DBAccess11.cpp: In member function ‘std::list<SiteCode> sqliteDB::GET_ALL_Site_Code()’:
DBAccess11.cpp:80:38: error: expected primary-expression before ‘Site_Code_list’
 for (auto& entry: list<SiteCode> Site_Code_list)
                                  ^
DBAccess11.cpp:80:38: error: expected ‘)’ before ‘Site_Code_list’
DBAccess11.cpp:80:52: error: expected ‘;’ before ‘)’ token
 for (auto& entry: list<SiteCode> Site_Code_list)
                                                ^

My Questions:

(1)Is this the right way to convert std::list into JSON using boost ? if NO then how should it be done ? (Note- I can only use boost and no other library )

(2) If my approach is right then what changes shall I make to correct it ?


Solution

  • 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 and data_type are configurable, but will usually be std::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"}