造轮子——C++实现一个Json库

.  前几天无意中看到我之前写的Json库,按今天的眼光去看简直稀烂2333,接口烂,实现烂,手一哆嗦就整个删掉了.

.  写C++程序的时候,有时候需要操作一些Json文件,这些Json仅作为配置使用,极少在业务逻辑里频繁操作,所以易用性是首要,性能是次要,我之前用cJSON库,无奈它是一个C风格的库,在C++代码里格格不入,即使将其封装后,看起来也十分蹩脚,我尝试写了一个比较符合C++风格的Json库,目前感觉还可以.

.  子节点由std::share_ptr管理,对Json的所有操作都被实现在Set/Get/Del函数里,从Json字符串序列化到对象则通过静态函数FromString实现,接口相当简洁.

接口清单:

Type()                          //  返回节点类型
ToInt()                         //  取int值
ToBool()                        //  取bool值
ToFloat()                       //  取float值
ToDouble()                      //  取double值
ToString()                      //  取string值
Set(val, key1, key2, ...)       //  设key...位置的节点
Get(key1, key2, ...)            //  取key...位置的节点
Del(key1, key2, ...)            //  删key...位置的节点
JValue::FromString(str)         //  从字符串解析json
JValue::FromFile(file)          //  从文件解析json
ToPrint()                       //  将节点序列化为可打印字符串

使用例子:

    //  从文件解析
    auto jfile = JValue::FromFile("a.json");
    //  从字符串解析
    auto jstring = JValue::FromString("{}");
    //  单独设值
    JValue json;
    json.Set(0);
    json.Set(true);
    json.Set("123");
    json.Set(JValue::Hash());
    json.Set(JValue::List());
    json.Set(JValue::FromString("{}"));

    //  读函数原型 JValue::Get(key1, key2, key3...)
    //  读 json["hash"]["k1"]
    json.Get("hash", "k1")->ToInt();
    //  读 json["list"][0]
    json.Get("list", 0)->ToInt();

    //  写函数原型 JValue::Set(val, key1, key2, key3...);
    //  写 json["list"] = []
    json.Set(JValue::List(), "list");
    //  写 json["list"][0] = 0
    json.Set(0, "list", 0);
    //  写 json["hash"] = {}
    json.Set(JValue::Hash(), "hash");
    //  写 json["hash"]["k"] = 0
    json.Set(0, "hash", "k");

    //  删函数原型 JValue::Del(key1, key2, key3...)
    //  删 json["hash"]["k"] = undefine
    json.Del("hash", "k");

    //  左值深拷贝
    {
        JValue json1, json2;
        json1 = json2;
    }

    //  右值拷贝
    {
        JValue json1, json2;
        json1 = std::move(json2);
    }

    //  序列化到字符串
    std::string str = json.ToPrint();

    //  范围遍历
    for (auto & jptr : json)
    { }

    //  索引遍历
    for (auto i = 0; i != json.GetCount(); ++i)
    {
        json.Get(i);
    }

    //  可结合C++标准算法
    auto it = std::find(std::begin(json), std::end(json), "key");

源码:

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "sformat.h"

//  条件判断, 抛出异常
#define DEBUG_CHECK(exp, type, ...) if (!exp) { throw type(__VA_ARGS__); }

//  异常说明, 显示Json错误位置往后20个字符
#define DEBUG_EXPINFO(exp, string) SFormat("{0}: {1}", exp, std::string(string).substr(0, 20))

class JValue {
public:
    using JValuePtr = std::shared_ptr;

    struct Parser {
        //  解析异常
        class Exception : public std::exception {
        public:
            Exception(const std::string & what): _what(what), std::exception(what.c_str())
            {}

        private:
            std::string _what;
        };

        static const char * SkipSpace(const char * string)
        {
            for (; *string != '\0' && *string <= 32; ++string)
                ;
            return string;
        }

        static const char * ParseList(const char * string, std::vector * output)
        {
            output->clear();
            while (*string != ']')
            {
                auto element = std::make_shared();
                string = Parser::Parse(string, element.get());
                string = SkipSpace(string);
                *string == ',' && ++string;
                string = SkipSpace(string);
                output->push_back(element);
            }
            return ++string;
        }

        static const char * ParseHash(const char * string, std::vector * output)
        {
            output->clear();
            while (*string != '}')
            {
                auto element = std::make_shared();
                DEBUG_CHECK((*string == '\"'), Parser::Exception, DEBUG_EXPINFO("Parse Hash Error: ", string));
                string = Parser::ParseString(string + 1, &element->_key);
                string = SkipSpace(string);
                DEBUG_CHECK((*string == ':'), Parser::Exception, DEBUG_EXPINFO("Parse Hash Error: ", string));
                string = Parser::Parse(string + 1, element.get());
                string = SkipSpace(string);
                *string == ',' && ++string;
                string = SkipSpace(string);
                output->push_back(element);
            }
            return ++string;
        }

        static const char * ParseString(const char * string, std::string * output)
        {
            output->clear();
            for (; *string != '\"'; ++string)
            {
                DEBUG_CHECK(*string != '\0', Parser::Exception, DEBUG_EXPINFO("Parse String Error", string));
                output->append(1, *string);
            }
            return string + 1;
        }

        static const char * ParseNumber(const char * string, double * output)
        {
            char value[64] = { 0 };
            for (auto i = 0; *string >= '0' && 
                             *string <= '9' || 
                             *string == '.'; ++i, ++string)
            {
                value[i] = *string;
            }
            *output = std::strtod(value, nullptr);
            return string;
        }

        static const char * ParseFalse(const char * string, double * output)
        {
            *output = 0;
            return string + 5;
        }

        static const char * ParseTrue(const char * string, double * output)
        {
            *output = 1;
            return string + 4;
        }

        static const char * Parse(const char * string, JValue * output)
        {
            string = Parser::SkipSpace(string);
            if (*string == '[')
            {
                string = ParseList(SkipSpace(string + 1), &output->_elements);
                output->_type = kLIST;
            }
            else if (*string == '{')
            {
                string = ParseHash(SkipSpace(string + 1), &output->_elements);
                output->_type = kHASH;
            }
            else if (*string == '\"')
            {
                string = ParseString(string + 1, &output->_string);
                output->_type = kSTRING;
            }
            else if (*string >= '0' && *string <= '9')
            {
                string = ParseNumber(string, &output->_number);
                output->_type = kNUMBER;
            }
            else if (string[0] == 't' && 
                     string[1] == 'r' && 
                     string[2] == 'u' && 
                     string[3] == 'e')
            {
                string = ParseTrue(string, &output->_number);
                output->_type = kBOOL;
            }
            else if (string[0] == 'f' && 
                     string[1] == 'a' && 
                     string[2] == 'l' && 
                     string[3] == 's' && 
                     string[4] == 'e')
            {
                string = ParseFalse(string, &output->_number);
                output->_type = kBOOL;
            }
            else
            {
                DEBUG_CHECK(false, Parser::Exception, DEBUG_EXPINFO("Parse Json Error", string));
            }
            return string;
        }
    };

public:
    enum JType {
        kNUMBER,
        kSTRING,
        kHASH,
        kLIST,
        kBOOL,
        kNULL,
    };

    struct Hash {};
    struct List {};

    //  如果是C++20, 可去掉这个模板
    template 
    struct remove_cvref {
        using type = std::remove_cv_t>;
    };

    template 
    using IsHash = std::is_same;

    template 
    using IsList = std::is_same;

    template 
    using IsBool = std::is_same;

    template 
    using IsJValue = std::is_same;

    template 
    using IsNull = std::is_same;

    template 
    struct IsString: public std::false_type {};
    template <>
    struct IsString: public std::true_type {};
    template 
    struct IsString: public std::true_type {};
    template <>
    struct IsString: public std::true_type {};

    template 
    struct IsNumber: public std::false_type {};
    template <>
    struct IsNumber: public std::true_type {};
    template <>
    struct IsNumber: public std::true_type {};

    template 
    struct IsInteger: public std::false_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};
    template <>
    struct IsInteger: public std::true_type {};

    static JValue FromFile(const std::string & fname)
    {
        std::ifstream ifile(fname);
        std::string buffer;
        std::copy(std::istream_iterator(ifile), 
                std::istream_iterator(), 
                std::back_inserter(buffer));
        return std::move(FromString(buffer));
    }

    static JValue FromString(const std::string & string)
    {
        JValue jvalue;
        try
        {
            Parser::Parse(string.c_str(), &jvalue);
        }
        catch (const Parser::Exception &)
        {
            jvalue.Set(nullptr);
        }
        return std::move(jvalue);
    }

    JValue(): _type(JType::kNULL)
    { }

    template 
    JValue(T && value)
    {
        Set(std::forward(value));
    }

    JValue(JValue && json)
    {
        *this = std::move(json);
    }

    JValue & operator =(const JValue & json)
    {
        return (*this = std::move(Clone(json)));
    }

    JValue & operator =(JValue && json)
    {
        _elements = std::move(json._elements);
        _number = std::move(json._number);
        _string = std::move(json._string);
        _type = std::move(json._type);
        _key = std::move(json._key);
        json._type = kNULL;
        return *this;
    }

    JType Type() const
    {
        return _type;
    }

    const std::string & Key() const
    {
        return _key;
    }

    int ToInt() const
    {
        return static_cast(_number);
    }

    float ToFloat() const
    {
        return static_cast(_number);
    }

    double ToDouble() const
    {
        return static_cast(_number);
    }

    bool ToBool() const
    {
        return _number != 0;
    }

    const std::string & ToString() const
    {
        return _string;
    }

    std::string ToPrint() const
    {
        std::string space;
        return std::move(Print(space));
    }

    size_t GetCount() const
    {
        return _elements.size();
    }

    std::vector::const_iterator begin() const
    {
        return _elements.begin();
    }

    std::vector::iterator begin()
    {
        return _elements.begin();
    }

    std::vector::const_iterator end() const
    {
        return _elements.end();
    }

    std::vector::iterator end()
    {
        return _elements.end();
    }

    template 
    JValuePtr Get(const Keys & ...keys)
    {
        return GetImpl(keys...);
    }

    template 
    bool Set(const char * val, const Keys & ...keys)
    {
        return SetImpl(val, keys...);
    }

    template 
    bool Set(Val && val, const Keys & ...keys)
    {
        return SetImpl(std::forward(val), keys...);
    }

    template 
    void Del(const Keys & ...keys)
    {
        DelImpl(keys...);
    }

private:
    JValue Clone(JValue && jvalue) const
    {
        return std::move(jvalue);
    }

    JValue Clone(const JValue & jvalue) const
    {
        JValue newval;
        switch (jvalue.Type())
        {
        case kNUMBER:
            {
                newval.Set(jvalue.ToDouble()); 
            }
            break;
        case kSTRING:
            {
                newval.Set(jvalue.ToString());
            }
            break;
        case kBOOL:
            {
                newval.Set(jvalue.ToBool());
            }
            break;
        case kHASH:
        case kLIST:
            {
                std::transform(std::cbegin(_elements),
                               std::cend(_elements),
                               std::back_inserter(newval._elements),
                               std::bind(&JValue::ClonePtr, this, std::placeholders::_1));
            }
            break;
        }
        newval._key = jvalue.Key();
        newval._type = jvalue.Type();
        return std::move(newval);
    }

    JValuePtr ClonePtr(const JValuePtr & ptr) const
    {
        return std::make_shared(std::move(Clone(*ptr)));
    }

    template 
    JValuePtr GetImpl(const Key & key, const Keys & ...keys)
    {
        auto jptr = GetImpl(key);
        if (jptr != nullptr)
        {
            return jptr->GetImpl(keys...);
        }
        return nullptr;
    }

    template 
    JValuePtr GetImpl(const Key & key)
    {
        if constexpr (IsInteger::value)
        {
            return (size_t)key < _elements.size()
                ? _elements.at(key)
                : nullptr;
        }
        if constexpr (IsString::value)
        {
            auto it = std::find(std::begin(_elements), std::end(_elements), key);
            return it != std::end(_elements)
                ? *it : nullptr;
        }
        return nullptr;
    }

    template 
    bool SetImpl(Val && val, const Key & key, const Keys & ...keys)
    {
        auto jptr = GetImpl(key);
        if (jptr != nullptr)
        {
            return jptr->SetImpl(std::forward(val), keys...);
        }
        return true;
    }

    template 
    bool SetImpl(Val && val, const Key & key)
    {
        if constexpr (IsString::value) { assert(Type() == kHASH); }
        if constexpr (IsInteger::value) { assert(Type() == kLIST); }
        auto jptr = GetImpl(key);
        if (jptr == nullptr)
        {
            auto newval = std::make_shared(std::forward(val));
            if constexpr (IsString::value)
            {
                newval->_key = key;
            }
            _elements.push_back(newval);
        }
        else
        {
            jptr->SetImpl(std::forward(val));
        }
        return true;
    }

    template 
    bool SetImpl(Val && val)
    {
        using Naked = remove_cvref::type;

        if constexpr (IsNumber::value || IsInteger::value)
        {
            _number = val;
            _elements.clear();
            _type = kNUMBER;
        }
        if constexpr (IsJValue::value)
        {
            *this = std::move(Clone(std::forward(val)));
        }
        if constexpr (IsString::value)
        {
            _string = std::forward(val);
            _elements.clear();
            _type = kSTRING;
        }
        if constexpr (IsBool::value)
        {
            _number = val ? 1 : 0;
            _elements.clear();
            _type = kBOOL;
        }
        if constexpr (IsHash::value)
        {
            _elements.clear();
            _type = kHASH;
        }
        if constexpr (IsList::value)
        {
            _elements.clear();
            _type = kLIST;
        }
        if constexpr (IsNull::value)
        {
            _elements.clear();
            _type = kNULL;
        }
        return true;
    }

    template 
    void DelImpl(const Key & key, const Keys & ...keys)
    {
        auto jptr = GetImpl(key);
        if (jptr != nullptr)
        {
            jptr->Del(keys...);
        }
    }

    template 
    void DelImpl(const Key & key)
    {
        if constexpr (IsInteger::value)
        {
            if (key < _elements.size())
            {
                _elements.erase(std::advance(std::begin(_elements), key));
            }
        }
        if constexpr (IsString::value)
        {
            auto it = std::find(std::begin(_elements), std::end(_elements), key);
            if (it != std::end(_elements)) 
            { 
                _elements.erase(it); 
            }
        }
    }

    std::string Print(std::string & space) const
    {
        switch (Type())
        {
        case kNUMBER:
            {
                return std::to_string(_number);
            }
            break;
        case kSTRING:
            {
                return "\"" + _string + "\"";
            }
            break;
        case kHASH:
            {
                std::vector strings;
                std::string resule("{\n");
                space.append("\t");
                resule.append(space);
                for (const auto & element : _elements)
                {
                    strings.push_back(SFormat("\"{0}\": {1}", 
                                              element->Key(), element->Print(space)));
                }
                resule.append(PrintJoin(strings, ",\n" + space));
                space.pop_back();
                resule.append("\n");
                resule.append(space);
                resule.append("}");
                return std::move(resule);
            }
            break;
        case kLIST:
            {
                std::vector strings;
                for (const auto & element : _elements)
                {
                    strings.push_back(element->Print(space));
                }
                return "[" + PrintJoin(strings, ", ") + "]";
            }
            break;
        case kBOOL:
            {
                return ToBool() ? "\"true\"" : "\"false\"";
            }
            break;
        }
        return "null";
    }

    static std::string PrintJoin(const std::vector & strings, const std::string & join)
    {
        size_t count = 0;
        for (const auto & string : strings)
        {
            count += string.size();
        }

        std::string resule;
        resule.reserve(count);

        if (!strings.empty())
        {
            resule.append(strings.at(0));

            for (auto i = 1; i != strings.size(); ++i)
            {
                resule.append(join);
                resule.append(strings.at(i));
            }
        }

        return resule;
    }

private:
    std::vector _elements;
    std::string _string;
    std::string _key;
    double _number;
    JType _type;

    friend struct Parser;
};

inline bool operator==(const JValue::JValuePtr & ptr, const std::string & key)
{
    return ptr->Key() == key;
}

inline bool operator!=(const JValue::JValuePtr & ptr, const std::string & key)
{
    return ptr->Key() != key;
}

从文件读取Json并打印:

auto json = std::move(JValue::FromFile("a.json"));
std::cout << json.ToPrint() << std::endl;
造轮子——C++实现一个Json库_第1张图片
打印结果
造轮子——C++实现一个Json库_第2张图片
a.json文件

你可能感兴趣的:(造轮子——C++实现一个Json库)