Json library implemented by boost variant

boost variant 介绍

boost variant 是一个不同union的泛型类,用于存储和操作不同类型但在使用时存在相同抽象的对象。variant 在实现不同类型的泛型的同时,提供对其包括的具体类型的安全访问。
基于此性质,boost variant 可以应用于json 这种数据结构,我们把json 中的Object, Array, String, Number, True, False, Null 统一当做同一种variant 类型。需要注意的是,json 中的Object 和 Array 类型是递归的variant 类型,在声明时需要使用 boost::recursive_wrapper 修饰。 boost::recursivee_wrapper 用于创建包含创建的variant类型的表达式.
在访问varint 类型时,可以使用 boost::get<T> 以及 boost::apply_visitor 的形式。
更多关于 boost variant 的介绍见
https://www.boost.org/doc/libs/1_62_0/doc/html/variant/reference.html

json 数据结构

json 的数据类型实现在 json_type.hpp

/*************************************************************************
    > File Name: json_type.hpp
    > Author: ce39906
    > Mail: ce39906@163.com
    > Created Time: 2018-07-31 16:25:59
 ************************************************************************/
#ifndef JSON_TYPE_HPP
#define JSON_TYPE_HPP
#include <boost/variant.hpp>
#include <string>
#include <unordered_map>
#include <vector>
namespace json
struct Object;
struct Array;
struct String
    String() {}
    String(const char* value) : value{value} {}
    String(std::string value) : value{std::move(value)} {}
    std::string value;
struct Number
    Number() {};
    Number(const double value) : value{value} {}
    double value;
struct True
struct False
struct Null
using Value = boost::variant<String,
                             Number,
                             boost::recursive_wrapper<Object>,
                             boost::recursive_wrapper<Array>,
                             True,
                             False,
                             Null>;
struct Object
    bool isMember(const std::string& key) const
        return values.count(key) != 0;
    const Value& at(const std::string& key) const
        return values.at(key);
    const Value& operator[](const std::string& key) const
        return values.at(key);
    std::unordered_map<std::string, Value> values;
struct Array
    const Value& at(const size_t idx) const
        return values.at(idx);
    const Value& operator[](const size_t idx) const
        return values.at(idx);
    size_t size() const
        return values.size();
    const Value& front() const
        return values.front();
    const Value& back() const
        return values.back();
    std::vector<Value> values;
} // ns json
#endif

json 数据访问

本节只介绍使用 boost::get<T> 访问varint 数据。 boost::apply_visitor 的方式在序列化的部分介绍
代码示例如下

namespace access
inline const Object& asObject(const Value& value)
    return boost::get<Object>(value);
inline const Array& asArray(const Value& value)
    return boost::get<Array>(value);
inline const String& asString(const Value& value)
    return boost::get<String>(value);
inline const Number& asNumber(const Value& value)
    return boost::get<Number>(value);
inline const True& asTrue(const Value& value)
    return boost::get<True>(value);
inline const False& asFalse(const Value& value)
    return boost::get<False>(value);
inline const Null& asNull(const Value& value)
    return boost::get<Null>(value);
} // ns access

json 序列化

json 序列化利用 boost::apply_visitor . boost::apply_visitor 需要实现一个visitor 函数对象,函数对象针对不同实际类型实现不同的序列化方式,针对Object以及Array 这两种类型需要递归调用visitor。
示例代码如下

/*************************************************************************
    > File Name: json_serialize.hpp
    > Author: ce39906
    > Mail: ce39906@163.com
    > Created Time: 2018-07-31 17:23:19
 ************************************************************************/
#ifndef JSON_SERIALIZE_HPP
#define JSON_SERIALIZE_HPP
#include "json_type.hpp"
#include "json_util.hpp"
#include <vector>
#include <ostream>
namespace json
struct SerializeToOstream : boost::static_visitor<void>
    explicit SerializeToOstream (std::ostream& out) : out(out) {}
    void operator() (const String& string) const
        out << "\"";
        out << string.value;
        out << "\"";
    void operator() (const Number& number) const
        out << util::cast::to_string_with_percision(number.value);
    void operator() (const Object& object) const
        out << "{";
        for (auto it = object.values.begin(); it != object.values.end();)
            out << "\"" << it->first << "\":";
            boost::apply_visitor(SerializeToOstream(out), it->second);
            if (++it != object.values.end())
                out << ",";
        out << "}";
    void operator() (const Array& array) const
        out << "[";
        for (auto it = array.values.cbegin(); it != array.values.cend();)
            boost::apply_visitor(SerializeToOstream(out), *it);
            if (++it != array.values.cend())
                out << ",";
        out << "]";
    void operator() (const True&) const
        out << "ture";
    void operator() (const False&) const
        out << "false";
    void operator() (const Null&) const
        out << "null";
  private:
    std::ostream& out;
struct SerializeToString : boost::static_visitor<void>
    explicit SerializeToString (std::string& out) : out(out) {}
    void operator() (const String& string) const
        out.push_back('\"');
        out.append(string.value);
        out.push_back('\"');
    void operator() (const Number& number) const
        const std::string number_str = util::cast::to_string_with_percision(number.value);
        out.append(std::move(number_str));
    void operator() (const Object& object) const
        out.push_back('{');
        for (auto it = object.values.begin(); it != object.values.end();)
            out.push_back('\"');
            out.append(it->first);
            out.push_back('\"');
            out.push_back(':');
            boost::apply_visitor(SerializeToString(out), it->second);
            if (++it != object.values.end())
                out.push_back(',');
        out.push_back('}');
    void operator() (const Array& array) const
        out.push_back('[');
        for (auto it = array.values.cbegin(); it != array.values.cend();)
            boost::apply_visitor(SerializeToString(out), *it);
            if (++it != array.values.cend())
                out.push_back(',');
        out.push_back(']');
    void operator() (const True&) const
        out.append("true");
    void operator() (const False&) const
        out.append("false");
    void operator() (const Null&) const
        out.append("null");
  private:
    std::string& out;
void serialize(std::ostream& out, const Object& object)
    Value value = object;
    boost::apply_visitor(SerializeToOstream(out), value);
void serialize(std::string& out, const Object& object)
    Value value = object;
    boost::apply_visitor(SerializeToString(out), value);
} // ns json
#endif

构造json 结构

针对String,Number,True,False,Null 这类简单类型可以直接使用构造函数构造。
Array 类型内部使用vector 类型,构造时使用vector 的 push_back, emplace_back 方法增加Array的元素。
Object 类型内部使用unordered_map 类型,构造时可以使用 unordered_map 的内建方法。
示例代码如下:

Object obj;
obj.values["string"] = "v1";
obj.values["bool"] = True();
obj.values["null"] = Null();
obj.values["number"] = Number(9);
Array arr;
arr.values.emplace_back(Number(1.02));
arr.values.emplace_back(Number(2.2));
arr.values.emplace_back(Number(3));
arr.values.emplace_back(True());
arr.values.emplace_back(False());
obj.values["array"] = std::move(arr);

示例代码测试构建json对象,访问json对象,以及序列化json 对象。
示例代码如下

/*************************************************************************
    > File Name: test_json.cpp
    > Author: ce39906
    > Mail: ce39906@163.com
    > Created Time: 2018-07-31 19:26:17
 ************************************************************************/
#include <iostream>
#include "json.hpp"
using namespace json;
int main()
    Object obj;
    obj.values["string"] = "v1";
    obj.values["bool"] = True();
    obj.values["null"] = Null();
    obj.values["number"] = Number(9);
    Array arr;
    arr.values.emplace_back(Number(1.02));
    arr.values.emplace_back(Number(2.2));
    arr.values.emplace_back(Number(3));
    arr.values.emplace_back(True());
    arr.values.emplace_back(False());
    obj.values["array"] = std::move(arr);
    // json access
    std::cout << "Test json access.\n";
    const auto& arr1 = access::asArray(obj["array"]);
    std::cout << "first number in arr is "
        << access::asNumber(arr1.front()).value << std::endl;
    // json serialize to ostream
    std::cout << "Test serialize to ostream.\n";
    serialize(std::cout, obj);
    std::cout << std::endl;
    // json serialize to string
    std::cout << "Test serialize to string.\n";
    std::string str;
    serialize(str, obj);
    std::cout << str << std::endl;
    return 0;
 

g++ –std=c++11 test_json.cpp -o test_json

执行结果如下
pic

使用boost spirit 实现json 的反序列化

https://github.com/ce39906/self-practices/tree/master/cppcode/variant_json

Json library implemented by boost variantboost variant 介绍boost variant 是一个不同union的泛型类,用于存储和操作不同类型但在使用时存在相同抽象的对象。variant 在实现不同类型的泛型的同时,提供对其包括的具体类型的安全访问。 基于此性质,boost variant 可以应用于json 这种数据结构,我们把j...
C++没有元对象,无法实现json和对象的映射(比如GSon),或者数据库的对象关系映射(比如GreenDao)。C++可以获取变量的字符串(#define name2str(name) (#name))和变量的类型(#define getType(value) typeid(value).name()),但是并没有办法遍历类的成员属性。解决办法是给类添加一个map,在构造函数中实现成员变量名称和成员变量类型的map,但这比较复杂。QT有QVariant,可以很好的实现映射。 QVariant与对象
Awesome list of C++ GameDev project A curated list of awesome C++ (mainly) things for Game Development. Inspired by awesome-... stuff. ~2000 projects listed here! If you want to add projects here, do a push request or open an issue! (Maybe some new cat
javascript模块by Kamlesh Chandnani 由Kamlesh Chandnani 了解JavaScript模块系统的基础知识,并建立自己的库 (Learn the basics of the JavaScript module system and build your own library) Lately we all have been hearing a lot ...
/************************************************************************* &amp;gt; File Name: cout_uint8.cpp &amp;gt; Author: liuce03 &amp;gt; Mail: liuce03@meituan.com &amp;gt; Creat...
两者都会调用移动构造 push 2我的理解就是有一个拷贝构造呀,然后使用placement new来进行移动构造 另外push也是能实现移动语义的吧,也有const左值引用和右值版本 c++11 右值引用,移动构造函数,emplace_back 解析 20要继续努力哦!: 那是不是这个意思? push_back和emplace_back在接受右值时,没啥区别,都是调用拷贝构造(c++11后 重载push_back 可接受右值 实现就是调用emplace_back) 但是我有个问题 push_back(2) 这个2是字面值吧?那就是有值?之前我一直以为是隐式的拷贝构造 现在有点疑惑?希望知道的大佬帮忙解决一下 对于左值的话,push_back只能调用拷贝构造,而emplace_back可以调用std::move函数实现移动语义 emmm 为啥push-back不行? 希望大佬们帮忙解决一下! c++11 右值引用,移动构造函数,emplace_back 解析 Nnboylhj: 这里有答案:c++中为什么push_back({1,2})可以,emplace_back({1,2})会报错? - 知乎 https://www.zhihu.com/question/438004429 linux shell 确保脚本只有一个运行实例 yong1585855343: 万一程序突发终止,由于 /tmp/lock.file 还在,下一次就没法启动了