美文网首页
造轮子——C++实现一个Json库

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

作者: 落单的毛毛虫 | 来源:发表于2018-06-29 02:53 被阅读0次

    .  前几天无意中看到我之前写的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 <string>
    #include <memory>
    #include <cassert>
    #include <fstream>
    #include <iterator>
    #include <algorithm>
    #include <functional>
    #include <type_traits>
    #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<JValue>;
    
        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<JValuePtr> * output)
            {
                output->clear();
                while (*string != ']')
                {
                    auto element = std::make_shared<JValue>();
                    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<JValuePtr> * output)
            {
                output->clear();
                while (*string != '}')
                {
                    auto element = std::make_shared<JValue>();
                    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 <class T>
        struct remove_cvref {
            using type = std::remove_cv_t<std::remove_reference_t<T>>;
        };
    
        template <class T>
        using IsHash = std::is_same<T, Hash>;
    
        template <class T>
        using IsList = std::is_same<T, List>;
    
        template <class T>
        using IsBool = std::is_same<T, bool>;
    
        template <class T>
        using IsJValue = std::is_same<T, JValue>;
    
        template <class T>
        using IsNull = std::is_same<T, nullptr_t>;
    
        template <class T>
        struct IsString: public std::false_type {};
        template <>
        struct IsString<char *>: public std::true_type {};
        template <int N>
        struct IsString<char[N]>: public std::true_type {};
        template <>
        struct IsString<std::string>: public std::true_type {};
    
        template <class T>
        struct IsNumber: public std::false_type {};
        template <>
        struct IsNumber<float>: public std::true_type {};
        template <>
        struct IsNumber<double>: public std::true_type {};
    
        template <class T>
        struct IsInteger: public std::false_type {};
        template <>
        struct IsInteger<std::int8_t>: public std::true_type {};
        template <>
        struct IsInteger<std::int16_t>: public std::true_type {};
        template <>
        struct IsInteger<std::int32_t>: public std::true_type {};
        template <>
        struct IsInteger<std::int64_t>: public std::true_type {};
        template <>
        struct IsInteger<std::uint8_t>: public std::true_type {};
        template <>
        struct IsInteger<std::uint16_t>: public std::true_type {};
        template <>
        struct IsInteger<std::uint32_t>: public std::true_type {};
        template <>
        struct IsInteger<std::uint64_t>: public std::true_type {};
    
        static JValue FromFile(const std::string & fname)
        {
            std::ifstream ifile(fname);
            std::string buffer;
            std::copy(std::istream_iterator<char>(ifile), 
                    std::istream_iterator<char>(), 
                    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 <class T>
        JValue(T && value)
        {
            Set(std::forward<T>(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<int>(_number);
        }
    
        float ToFloat() const
        {
            return static_cast<float>(_number);
        }
    
        double ToDouble() const
        {
            return static_cast<double>(_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<JValuePtr>::const_iterator begin() const
        {
            return _elements.begin();
        }
    
        std::vector<JValuePtr>::iterator begin()
        {
            return _elements.begin();
        }
    
        std::vector<JValuePtr>::const_iterator end() const
        {
            return _elements.end();
        }
    
        std::vector<JValuePtr>::iterator end()
        {
            return _elements.end();
        }
    
        template <class ...Keys>
        JValuePtr Get(const Keys & ...keys)
        {
            return GetImpl(keys...);
        }
    
        template <class ...Keys>
        bool Set(const char * val, const Keys & ...keys)
        {
            return SetImpl(val, keys...);
        }
    
        template <class Val, class ...Keys>
        bool Set(Val && val, const Keys & ...keys)
        {
            return SetImpl(std::forward<Val>(val), keys...);
        }
    
        template <class ...Keys>
        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<JValue>(std::move(Clone(*ptr)));
        }
    
        template <class Key, class ...Keys>
        JValuePtr GetImpl(const Key & key, const Keys & ...keys)
        {
            auto jptr = GetImpl(key);
            if (jptr != nullptr)
            {
                return jptr->GetImpl(keys...);
            }
            return nullptr;
        }
    
        template <class Key>
        JValuePtr GetImpl(const Key & key)
        {
            if constexpr (IsInteger<Key>::value)
            {
                return (size_t)key < _elements.size()
                    ? _elements.at(key)
                    : nullptr;
            }
            if constexpr (IsString<Key>::value)
            {
                auto it = std::find(std::begin(_elements), std::end(_elements), key);
                return it != std::end(_elements)
                    ? *it : nullptr;
            }
            return nullptr;
        }
    
        template <class Val, class Key, class ...Keys>
        bool SetImpl(Val && val, const Key & key, const Keys & ...keys)
        {
            auto jptr = GetImpl(key);
            if (jptr != nullptr)
            {
                return jptr->SetImpl(std::forward<Val>(val), keys...);
            }
            return true;
        }
    
        template <class Val, class Key>
        bool SetImpl(Val && val, const Key & key)
        {
            if constexpr (IsString<Key>::value) { assert(Type() == kHASH); }
            if constexpr (IsInteger<Key>::value) { assert(Type() == kLIST); }
            auto jptr = GetImpl(key);
            if (jptr == nullptr)
            {
                auto newval = std::make_shared<JValue>(std::forward<Val>(val));
                if constexpr (IsString<Key>::value)
                {
                    newval->_key = key;
                }
                _elements.push_back(newval);
            }
            else
            {
                jptr->SetImpl(std::forward<Val>(val));
            }
            return true;
        }
    
        template <class Val>
        bool SetImpl(Val && val)
        {
            using Naked = remove_cvref<Val>::type;
    
            if constexpr (IsNumber<Naked>::value || IsInteger<Naked>::value)
            {
                _number = val;
                _elements.clear();
                _type = kNUMBER;
            }
            if constexpr (IsJValue<Naked>::value)
            {
                *this = std::move(Clone(std::forward<Val>(val)));
            }
            if constexpr (IsString<Naked>::value)
            {
                _string = std::forward<Val>(val);
                _elements.clear();
                _type = kSTRING;
            }
            if constexpr (IsBool<Naked>::value)
            {
                _number = val ? 1 : 0;
                _elements.clear();
                _type = kBOOL;
            }
            if constexpr (IsHash<Naked>::value)
            {
                _elements.clear();
                _type = kHASH;
            }
            if constexpr (IsList<Naked>::value)
            {
                _elements.clear();
                _type = kLIST;
            }
            if constexpr (IsNull<Naked>::value)
            {
                _elements.clear();
                _type = kNULL;
            }
            return true;
        }
    
        template <class Key, class ...Keys>
        void DelImpl(const Key & key, const Keys & ...keys)
        {
            auto jptr = GetImpl(key);
            if (jptr != nullptr)
            {
                jptr->Del(keys...);
            }
        }
    
        template <class Key>
        void DelImpl(const Key & key)
        {
            if constexpr (IsInteger<Key>::value)
            {
                if (key < _elements.size())
                {
                    _elements.erase(std::advance(std::begin(_elements), key));
                }
            }
            if constexpr (IsString<Key>::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<std::string> 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<std::string> 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<std::string> & 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<JValuePtr> _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;
    
    打印结果 a.json文件

    相关文章

      网友评论

          本文标题:造轮子——C++实现一个Json库

          本文链接:https://www.haomeiwen.com/subject/oeroyftx.html