json 对c++类的序列化(自动生成代码)
【动机】
之前写网络协议的时候,使用的是google protobuf,protobuf不但在性能和扩展性上有很好的优势,protoc自动生成c++类代码的工具,这点确实给程序员带来了很多便利。
做后面一项目使用的json格式来传输,然后就萌生了实现像protoc这样的工具,根据json文件来生成c++类代码,并且生成序列化代码,这样在写网络的时候就无需把jsonvalue序列化散落在各处。
【思路】
之前写object-c的时候,如果你要对类的序列化,你必须实现NSCoding协议(接口), 这样的实现方式很友好,同样,我的方式则是参考NSCoding的,
【Code】
我这里借鉴网友的一个json序列化实现方式,感觉这种方式很方便,他是通过模板来实现对内置类型的识别,而且对所有的类型都使用了统一接口:
/* * Copyright (c) 2011-2012 Promit Roy * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ ////////////////////////////////////////////////////////////// // From: http://ventspace.wordpress.com/2012/10/08/c-json-serialization/ // FileName: json_serializer_helper.hpp // Modify: Sachin // Date: 2013/9/22 13:41 // Description: // // History: // <author> <time> <descript> // Sachin 2013/9/22 add ////////////////////////////////////////////////////////////// #ifndef JSON_SERIALIZER_HELPER_HPP #define JSON_SERIALIZER_HELPER_HPP #include <lib_json/json_lib.h> #include <boost/utility.hpp> #include <boost/type_traits.hpp> #include <string> #include <assert.h> #define NVP(name) #name, name #define SerializeNVP(name) Serialize(NVP(name)) #define DeSerializeNVP(name) DeSerialize(NVP(name)) class JsonSerializerHelper { private: //SFINAE garbage to detect whether a type has a Serialize member struct serialize_not_found {}; typedef serialize_not_found SerializeNotFound; struct SerializeFound { char x[2]; }; template<typename T, void (T::*)(JsonSerializerHelper&) const> struct SerializeTester { }; template<typename T> static SerializeFound SerializeTest(SerializeTester<T, &T::Serialize>*); template<typename T> static SerializeNotFound SerializeTest(...); template<typename T> struct HasSerialize { static const bool value = sizeof(SerializeTest<T>(0)) == sizeof(SerializeFound); }; //Serialize using a free function defined for the type (default fallback) template<typename TValue> void SerializeImpl(const TValue& value, typename boost::disable_if<HasSerialize<const TValue> >::type* dummy = 0) { //prototype for the serialize free function, so we will get a link error if it\'s missing //this way we don\'t need a header with all the serialize functions for misc types (eg math) void SerializeFail(const TValue&, JsonSerializerHelper&); SerializeFail(value, *this); } //Serialize using a member function Serialize(JsonSerializer&) template<typename TValue> void SerializeImpl(const TValue& value, typename boost::enable_if<HasSerialize<const TValue> >::type* dummy = 0) { value.Serialize(*this); } private: // struct deserialize_not_found {}; typedef deserialize_not_found DeSerializeNotFound; struct DeSerializeFound { char x[2]; }; template<typename T, void (T::*)(const JsonSerializerHelper&)> struct DeSerializeTester { }; template<typename T> static DeSerializeFound DeSerializeTest(DeSerializeTester<T, &T::DeSerialize>*); template<typename T> static DeSerializeNotFound DeSerializeTest(...); template<typename T> struct HasDeSerialize { static const bool value = sizeof(DeSerializeTest<T>(0)) == sizeof(DeSerializeFound); }; //Serialize using a free function defined for the type (default fallback) template<typename TValue> void DeSerializeImpl(TValue& value, typename boost::disable_if<HasDeSerialize<TValue> >::type* dummy = 0) const { void DeSerializeFail(TValue&, const JsonSerializerHelper&); DeSerializeFail(value, *this); } //Serialize using a member function Serialize(JsonSerializer&) template<typename TValue> void DeSerializeImpl(TValue& value, typename boost::enable_if<HasDeSerialize<TValue> >::type* dummy = 0) const { value.DeSerialize(*this); } public: JsonSerializerHelper() { } template<typename TKey, typename TValue> void Serialize(TKey key, const TValue& value, typename boost::enable_if<boost::is_class<TValue> >::type* dummy = 0) { // class to json JsonSerializerHelper subVal; subVal.SerializeImpl(value); JsonValue[key] = subVal.JsonValue; } template<typename TKey, typename TValue> void DeSerialize(TKey key, TValue& value, typename boost::enable_if<boost::is_class<TValue> >::type* dummy = 0) const { // json to class JsonSerializerHelper subVal; subVal.JsonValue = JsonValue[key]; subVal.DeSerializeImpl(value); } template<typename TKey> void Serialize(TKey key, const Json::Value& value) { Write(key, value); } template<typename TKey> void DeSerialize(TKey key, Json::Value& value) const { Read(key, value); } //Serialize a string value template<typename TKey> void Serialize(TKey key, const std::string& value) { Write(key, value); } //DeSerialize a string value template<typename TKey> void DeSerialize(TKey key, std::string& value) const { Read(key, value); } //Serialize a non class type directly using JsonCpp template<typename TKey, typename TValue> void Serialize(TKey key, const TValue& value, typename boost::enable_if<boost::is_fundamental<TValue> >::type* dummy = 0) { Write(key, value); } template<typename TKey, typename TValue> void DeSerialize(TKey key, TValue& value, typename boost::enable_if<boost::is_fundamental<TValue> >::type* dummy = 0) const { Read(key, value); } //Serialize an enum type to JsonCpp template<typename TKey, typename TEnum> void Serialize(TKey key, const TEnum& value, typename boost::enable_if<boost::is_enum<TEnum> >::type* dummy = 0) { int ival = (int) value; Write(key, ival); } template<typename TKey, typename TEnum> void DeSerialize(TKey key, TEnum& value, typename boost::enable_if<boost::is_enum<TEnum> >::type* dummy = 0) const { int ival = (int) value; Read(key, ival); value = (TEnum) ival; } template<typename TKey, typename TValue> void Serialize(TKey key, const std::vector<TValue>& vec) { Write(key, vec.begin(), vec.end()); } template<typename TKey, typename TValue> void DeSerialize(TKey key, std::vector<TValue>& vec) const { JsonSerializerHelper subVal; subVal.JsonValue = JsonValue[key]; subVal.Read(vec); } Json::Value JsonValue; private: template<typename TKey, typename TValue> void Write(TKey key, const TValue& value) { JsonValue[key] = value; } template<typename TKey, typename TValue> void Write(TKey key, const std::vector<TValue>& vec) { JsonSerializerHelper subVal; int index = 0; for(typename std::vector<TValue>::const_iterator it = vec.begin(); it != vec.end(); ++it) { subVal.Serialize(index, *it); ++index; } JsonValue[key] = subVal.JsonValue; } template<typename TKey, typename TItor> void Write(TKey key, TItor first, TItor last) { JsonSerializerHelper subVal; int index = 0; for(TItor it = first; it != last; ++it) { subVal.Serialize(index, *it); ++index; } JsonValue[key] = subVal.JsonValue; } template<typename TKey, typename TValue> void Read(TKey key, TValue& value, typename boost::enable_if<boost::is_arithmetic<TValue> >::type* dummy = 0) const { int ival = 0 ; if (JsonValue[key].isNumeric()){ ival = JsonValue[key].asInt(); } else { //assert(false); } value = (TValue) ival; } template<typename TKey, typename TValue> void Read(TKey key, TValue& value) const { value = JsonValue[key]; } template<typename TKey> void Read(TKey key, bool& value) const { bool bval = false ; bval = JsonValue[key].asBool(); value = bval; } template<typename TKey> void Read(TKey key, int& value) const { int ival = 0 ; if (JsonValue[key].isNumeric()){ ival = JsonValue[key].asInt(); } else if (JsonValue[key].isString()){ ival = atoi(JsonValue[key].asCString()); } else { //assert(false); } value = ival; } template<typename TKey> void Read(TKey key, unsigned int& value) const { unsigned int uival = 0 ; if (JsonValue[key].isNumeric()){ uival = JsonValue[key].asUInt(); } else if (JsonValue[key].isString()){ uival = atoi(JsonValue[key].asCString()); } else { //assert(false); } value = uival; } template<typename TKey> void Read(TKey key, float& value) const { float fval = 0.0 ; if (JsonValue[key].isNumeric()){ fval = JsonValue[key].asFloat(); } else if (JsonValue[key].isString()){ fval = atof(JsonValue[key].asCString()); } else { //assert(false); } value = fval; } template<typename TKey> void Read(TKey key, double& value) const { double dval = 0.0 ; if (JsonValue[key].isNumeric()){ dval = JsonValue[key].asDouble(); } else if (JsonValue[key].isString()){ dval = atof(JsonValue[key].asCString()); } else { //assert(false); } value = dval; } template<typename TKey> void Read(TKey key, std::string& value) const { std::string sval = "" ; if (JsonValue[key].isString()){ sval = JsonValue[key].asString(); } else { //assert(false); } value = sval; } template<typename TValue> void Read(std::vector<TValue>& vec) const { if(!JsonValue.isArray()) return; vec.clear(); vec.reserve(vec.size() + JsonValue.size()); for(int i = 0; i < JsonValue.size(); ++i) { TValue val; DeSerialize(i, val); vec.push_back(val); } } }; #endif //JSON_SERIALIZER_HELPER_HPP
我对原来的基础上进行了稍微改良,把DeSerialize 和Serialize分离了,每个类需要实现这两个接口,这个工具通过函数重载模板匹配对序列化做了统一的接口
【生成Cpp代码】
我们对json文件进行解析,json的dict对应c++的类,字典类的某个字段相应的类型对应c++类型,用key值作为class类型名,
【样例】
json
{ "image": { "width": 800, "height": 600, "title": "View from 15th Floor", "thumbnail": { "url": "http://www.example.com/image/481989943", "height": 125, "width": "100" }, "ids": [116, 943, 234, 38793] } }
c++ cpp
// Don\'t Edit it #ifndef TEST_H_ #define TEST_H_ #include <string> #include <vector> #include <lib_json/json_lib.h> class JsonSerializerHelper; namespace net { namespace test { class Thumbnail { public: Thumbnail(); ~Thumbnail(){} const std::string& get_url() { return url;} const std::string& get_url() const { return url;} void set_url(const std::string& url_a) { url = url_a; } const std::string& get_width() { return width;} const std::string& get_width() const { return width;} void set_width(const std::string& width_a) { width = width_a; } const int& get_height() { return height;} const int& get_height() const { return height;} void set_height(const int& height_a) { height = height_a; } void Serialize(JsonSerializerHelper& json_serializer_helper) const; void DeSerialize(const JsonSerializerHelper& json_serializer_helper); private: std::string url; std::string width; int height; }; // class Thumbnail class Image { public: Image(); ~Image(){} const std::vector<int>& get_ids() { return ids;} const std::vector<int>& get_ids() const { return ids;} void set_ids(const std::vector<int>& ids_a) { ids = ids_a; } const int& get_width() { return width;} const int& get_width() const { return width;} void set_width(const int& width_a) { width = width_a; } const std::string& get_title() { return title;} const std::string& get_title() const { return title;} void set_title(const std::string& title_a) { title = title_a; } Thumbnail& get_thumbnail() { return thumbnail;} const Thumbnail& get_thumbnail() const { return thumbnail;} void set_thumbnail(const Thumbnail& thumbnail_a) { thumbnail = thumbnail_a; } const int& get_height() { return height;} const int& get_height() const { return height;} void set_height(const int& height_a) { height = height_a; } void Serialize(JsonSerializerHelper& json_serializer_helper) const; void DeSerialize(const JsonSerializerHelper& json_serializer_helper); private: std::vector<int> ids; int width; std::string title; Thumbnail thumbnail; int height; }; // class Image class Test { public: Test(); ~Test(){} Image& get_image() { return image;} const Image& get_image() const { return image;} void set_image(const Image& image_a) { image = image_a; } void Serialize(JsonSerializerHelper& json_serializer_helper) const; void DeSerialize(const JsonSerializerHelper& json_serializer_helper); private: Image image; }; // class Test } // namespace test } // namespace net #endif // TEST_H_
//Don\'t Edit it #include "test.h" #include "network/net_util/json_serializer_helper.hpp" namespace net { namespace test { Thumbnail::Thumbnail(): height(0){ } void Thumbnail::Serialize( JsonSerializerHelper& json_serializer_helper) const { json_serializer_helper.SerializeNVP(url); json_serializer_helper.SerializeNVP(width); json_serializer_helper.SerializeNVP(height); } void Thumbnail::DeSerialize( const JsonSerializerHelper& json_serializer_helper) { json_serializer_helper.DeSerializeNVP(url); json_serializer_helper.DeSerializeNVP(width); json_serializer_helper.DeSerializeNVP(height); } Image::Image(): width(0), height(0){ } void Image::Serialize( JsonSerializerHelper& json_serializer_helper) const { json_serializer_helper.SerializeNVP(ids); json_serializer_helper.SerializeNVP(width); json_serializer_helper.SerializeNVP(title); json_serializer_helper.SerializeNVP(thumbnail); json_serializer_helper.SerializeNVP(height); } void Image::DeSerialize( const JsonSerializerHelper& json_serializer_helper) { json_serializer_helper.DeSerializeNVP(ids); json_serializer_helper.DeSerializeNVP(width); json_serializer_helper.DeSerializeNVP(title); json_serializer_helper.DeSerializeNVP(thumbnail); json_serializer_helper.DeSerializeNVP(height); } Test::Test(){ } void Test::Serialize( JsonSerializerHelper& json_serializer_helper) const { json_serializer_helper.SerializeNVP(image); } void Test::DeSerialize( const JsonSerializerHelper& json_serializer_helper) { json_serializer_helper.DeSerializeNVP(image); } } // namespace test } // namespace net
【注意】
1.因为json支持数组支持不同类型的值,但c++不支持,所有我这里数组里面只认识写简单的类型,但要类型一致,
2.这里用key的首字母大写做类型名,用key值作为对象名,所以一定要保证首字母key是小写的
3.因为不同json可能有相同的key值,所以我针对不同的json加了namespace 保护
以上的问题,我们在开发过程中,这样的需求很少,即使有,我们也去避开这样的问题。不是去正面对抗这样的问题。因为我要自动生成解析代码,自动生成好处是自动更新,无需人为参与。
【GitHub】
https://github.com/SachinKung/json2cpp