Last active
April 4, 2017 19:59
-
-
Save kevinkreiser/bee394c60c615e0acdad to your computer and use it in GitHub Desktop.
Serialize Json with C++11 Initializer List Syntax
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Build this with something like: g++ json_serializer.cpp -std=c++11 -I/usr/include | |
//Requires you at least have boost base library | |
#include <ostream> | |
#include <boost/variant.hpp> | |
#include <memory> | |
#include <string> | |
#include <cinttypes> | |
#include <cstddef> | |
#include <unordered_map> | |
#include <vector> | |
#include <sstream> | |
#include <iomanip> | |
#include <iostream> | |
namespace json { | |
//forward declare a little bit | |
class jmap; | |
using map_ptr = std::shared_ptr<jmap>; | |
class jarray; | |
using array_ptr = std::shared_ptr<jarray>; | |
//a variant of all the possible values to go with keys in json | |
using value = boost::variant<std::string, uint64_t, int64_t, long double, bool, std::nullptr_t, map_ptr, array_ptr>; | |
//the map value type in json | |
class jmap : public std::unordered_map<std::string, value> { | |
public: | |
//just specialize unoredered_map | |
using std::unordered_map<std::string, value>::unordered_map; | |
//and be able to spit out text | |
friend std::ostream& operator<<(std::ostream& stream, const jmap& json); | |
}; | |
//the array value type in json | |
class jarray : public std::vector<value> { | |
public: | |
//just specialize vector | |
using std::vector<value>::vector; | |
protected: | |
//and be able to spit out text | |
friend std::ostream& operator<<(std::ostream& stream, const jarray& json); | |
}; | |
//how we serialize the different primitives to string | |
class ostream_visitor : public boost::static_visitor<std::ostream&> | |
{ | |
public: | |
ostream_visitor(std::ostream& o):ostream_(o), fill(o.fill()){} | |
std::ostream& operator()(const std::string& value) const { | |
ostream_ << '"'; | |
//TODO: this may need to get more complicated | |
for (const auto& c : value) { | |
switch (c) { | |
case '\\': ostream_ << "\\\\"; break; | |
case '"': ostream_ << "\\\""; break; | |
case '/': ostream_ << "\\/"; break; | |
case '\b': ostream_ << "\\b"; break; | |
case '\f': ostream_ << "\\f"; break; | |
case '\n': ostream_ << "\\n"; break; | |
case '\r': ostream_ << "\\r"; break; | |
case '\t': ostream_ << "\\t"; break; | |
default: | |
if(c >= 0 && c < 32) { | |
//format changes for json hex | |
ostream_.setf(std::ios::hex, std::ios::basefield); | |
ostream_.setf(std::ios::uppercase); | |
ostream_.fill('0'); | |
//output hex | |
ostream_ << "\\u" << std::setw(4) << static_cast<int>(c); | |
//tear down format changes | |
ostream_.unsetf(std::ios::basefield); | |
ostream_.unsetf(std::ios::uppercase); | |
ostream_.fill(fill); | |
} | |
else | |
ostream_ << c; | |
break; | |
} | |
} | |
return ostream_ << '"'; | |
} | |
std::ostream& operator()(uint64_t value) const { return ostream_ << value; } | |
std::ostream& operator()(int64_t value) const { return ostream_ << value; } | |
std::ostream& operator()(long double value) const { return ostream_ << value; } | |
std::ostream& operator()(bool value) const { return ostream_ << (value ? "true" : "false"); } | |
std::ostream& operator()(std::nullptr_t value) const { return ostream_ << "null"; } | |
std::ostream& operator()(const map_ptr& value) const { return ostream_ << *value; } | |
std::ostream& operator()(const array_ptr& value) const { return ostream_ << *value; } | |
private: | |
std::ostream& ostream_; | |
char fill; | |
}; | |
std::ostream& operator<<(std::ostream& stream, const jmap& json){ | |
stream << '{'; | |
bool seprator = false; | |
for(const auto& key_value : json) { | |
if(seprator) | |
stream << ','; | |
seprator = true; | |
stream << '"' << key_value.first << "\":"; | |
boost::apply_visitor(ostream_visitor(stream), key_value.second); | |
} | |
stream << '}'; | |
return stream; | |
} | |
std::ostream& operator<<(std::ostream& stream, const jarray& json){ | |
stream << '['; | |
bool seprator = false; | |
for(const auto& element : json) { | |
if(seprator) | |
stream << ','; | |
seprator = true; | |
boost::apply_visitor(ostream_visitor(stream), element); | |
} | |
stream << ']'; | |
return stream; | |
} | |
map_ptr map(std::initializer_list<jmap::value_type> list) { | |
return map_ptr(new jmap(list)); | |
} | |
array_ptr array(std::initializer_list<jarray::value_type> list) { | |
return array_ptr(new jarray(list)); | |
} | |
} | |
int main(void) { | |
//construct some json that looks like OSRM output | |
using namespace std; | |
auto json = json::map | |
({ | |
{"hint_data", json::map | |
({ | |
{"locations", json::array | |
({ | |
string("_____38_SADaFQQAKwEAABEAAAAAAAAAdgAAAFfLwga4tW0C4P6W-wAARAA"), | |
string("fzhIAP____8wFAQA1AAAAC8BAAAAAAAAAAAAAP____9Uu20CGAiX-wAAAAA") | |
}) | |
}, | |
{"checksum", static_cast<uint64_t>(2875622111)} | |
}) | |
}, | |
{"route_name", json::array({string("West 26th Street"), string("Madison Avenue")})}, | |
{"via_indices", json::array({uint64_t(0), uint64_t(9)})}, | |
{"found_alternative", bool(false)}, | |
{"route_summary", json::map | |
({ | |
{"end_point", string("West 29th Street")}, | |
{"start_point", string("West 26th Street")}, | |
{"total_time", uint64_t(145)}, | |
{"total_distance", uint64_t(878)} | |
}) | |
}, | |
{"via_points", json::array | |
({ | |
json::array({(long double)(40.744377), (long double)(-73.990433)}), | |
json::array({(long double)(40.745811), (long double)(-73.988075)}) | |
}) | |
}, | |
{"route_instructions", json::array | |
({ | |
json::array({ string("10"), string("West 26th Street"), uint64_t(216), uint64_t(0), uint64_t(52), string("215m"), string("SE"), uint64_t(118) }), | |
json::array({ string("1"), string("East 26th Street"), uint64_t(153), uint64_t(2), uint64_t(29), string("153m"), string("SE"), uint64_t(120) }), | |
json::array({ string("7"), string("Madison Avenue"), uint64_t(237), uint64_t(3), uint64_t(25), string("236m"), string("NE"), uint64_t(29) }), | |
json::array({ string("7"), string("East 29th Street"), uint64_t(155), uint64_t(6), uint64_t(29), string("154m"), string("NW"), uint64_t(299) }), | |
json::array({ string("1"), string("West 29th Street"), uint64_t(118), uint64_t(7), uint64_t(21), string("117m"), string("NW"), uint64_t(299) }), | |
json::array({ string("15"), string(""), uint64_t(0), uint64_t(8), uint64_t(0), string("0m"), string("N"), uint64_t(0) }) | |
}) | |
}, | |
{"route_geometry", string("ozyulA~p_clCfc@ywApTar@li@ybBqe@c[ue@e[ue@i[ci@dcB}^rkA")}, | |
{"status_message", string("Found route between points")}, | |
{"status", uint64_t(0)}, | |
{"escaped_string", string("\"\t\r\n\\\a")} | |
}); | |
//serialize it | |
std::cout << *json; | |
std::cout << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment