强大的boost

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 以及 boost::apply_visitor 的形式。
更多关于 boost variant 的介绍见:
https://www.boost.org/doc/libs/1_62_0/doc/html/variant/reference.html

json 数据结构

json 的数据类型实现如下

/*************************************************************************
    > 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 访问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

TODO

使用boost spirit 实现json反序列化

完整代码

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