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_

【注意】

  1.因为json支持数组支持不同类型的值,但c++不支持,所有我这里数组里面只认识写简单的类型,但要类型一致,

2.这里用key的首字母大写做类型名,用key值作为对象名,所以一定要保证首字母key是小写的

3.因为不同json可能有相同的key值,所以我针对不同的json加了namespace 保护

以上的问题,我们在开发过程中,这样的需求很少,即使有,我们也去避开这样的问题。不是去正面对抗这样的问题。因为我要自动生成解析代码,自动生成好处是自动更新,无需人为参与。

【GitHub】

https://github.com/SachinKung/json2cpp

  

json 对c++类的序列化(自动生成代码),布布扣,bubuko.com

时间: 2024-10-12 17:31:10

json 对c++类的序列化(自动生成代码)的相关文章

跟我一起学extjs5(35--单个模块的设计[3根据类的标注自动生成数据])

跟我一起学extjs5(35--单个模块的设计[3根据类的标注自动生成数据]) 然后在hibernate.cfg.xml中加入: <mapping class="com.jfok.server.hibernate.system._ModuleField" /> <mapping class="com.jfok.server.hibernate.system._ModuleGridScheme" /> <mapping class=&qu

在Spring Boot中使用MyBatis并且自动生成代码,详细一看就懂

MyBatis目前是主流的数据访问层框架,我司目前的项目大部分都是用MyBatis.本文将使用Spring Boot快速创建项目,并且在Spring Boot中使用MyBatis,编写了一个根据区域id获取区域信息的接口例子.在最后,使用MyBatis的Generator自动生成代码. 0.新建Spring Boot项目 打开开发工具:IntelliJ IDEA,选择jdk 1.8以上. 点击File→New→Project...,选择Spring Initializr. 然后next: nex

Mybatis 自动生成代码,数据库postgresql

最近做了一个项目,使用Mybatis自动生成代码,下面做一下总结,被以后参考: 一.提前准备: 1.工具类:mybatis-generator-core-1.3.2.jar 2.postgresql驱动:postgresql-9.2-1003-jdbc4.jar 3.xml文件 这些我都上传到了附件上,下载链接:Download 二.XML详解 咱们的核心配置文件:mybatisGeneratorConfig.xml <?xml version="1.0" encoding=&q

【转】Mybits自动生成代码

一.构建一个环境 1. 首先创建一个表: [sql] view plaincopy CREATE TABLE t_user ( USER_ID INT NOT NULL AUTO_INCREMENT, USER_NAME CHAR(30) NOT NULL, USER_PASSWORD CHAR(10) NOT NULL, USER_EMAIL CHAR(30) NOT NULL, PRIMARY KEY (USER_ID) ) ENGINE=InnoDB DEFAULT CHARSET=utf

maven插件mybatis-generator自动生成代码

在开发中ssm框架用的十分广泛.mybatis最为持久层框架,根据xml.或者注解映射数据.自己可以控制sql,灵活简单操作数据库.但是,所有的sql文件都是有自己编写,不仅繁琐,而且很耗时,在开发中,速度.效率很重要.所以很多基础sql是有规律可循,可以根据数据库字段自动生成的.下面就进入今天的主题,通过maven插件mybatis-generator自动生成代码. 1.环境配置,创建maven项目,在pom.xml添加插件配置. <build> <finalName>zsxt&

Mybatis自动生成代码

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文将简要介绍怎样利用Mybatis Generator自动生成Mybatis的相关代码,Mybatis Generator是一个非常好用的工具,使用它可以大大节省开发的时间,并减少代码的编写量. 一.构建一个环境 1. 首先创建一个表: CREATE TABLE t_user ( USER_ID INT NOT NULL AUTO_INCREMENT, USER_NAME CHAR(

Java进阶之 如何自动生成代码

一.前言:为什么要有代码的自动生成? 对于这个问题 最简洁直接的回答就是:代替手动编写代码.提高工作效率. 什么样的场景和代码适合用自动生成这种方式呢? 做过Java服务端的朋友一定都知道代码中我们需要编写与数据库表映射的Java实体类(Entity).需要编写与实体对应的DAO类(XxDao.java类中有包含对应实体的增.删.改.查基本操作).在这些实体类中通常都是一些属性方法以及属性对应的get/set方法.而实体对应的DAO类中也基本会包含有增.删.改.查这些与数据库操作相关的方法.在编

Mybaits Generator自动生成代码

终于有空看看自动生成代码这块.看起容易上手,但功能强大. 下载Mybaits Generator:https://github.com/mybatis/generator/releases 这就是个单独的工具,也有集成到别的工具里的,先不管,用单独的试下.关键是用到这个JAR:mybatis-generator-core-1.3.2.jar  单独拿出来用就可以. 另外就是需要一个连接数据库的驱动程序,我用的是mongodb数据库,驱动是:mysql-connector-java-3.1.13-

[06] 利用mybatis-generator自动生成代码

1.mybatis-generator 概述 MyBatis官方提供了逆向工程 mybatis-generator,可以针对数据库表自动生成MyBatis执行所需要的代码(如Mapper.java.Mapper.xml.POJO).mybatis-generator 有三种用法:命令行.eclipse插件.maven插件.而maven插件的方式比较通用,本文也将概述maven插件的使用方式. 2.pom.xml中配置plugin (官方文档:Running MyBatis Generator W