低版本C++ Json反序列化方案

现在的API接口普遍使用Json语法来描述,API的返回通常使用类似这样的结构:

{
  "code": 0,
  "message": "",
  "data": {...}
}

即所有API都有code和message属性以表示成功或错误,它和业务是无关的;其它属性/数据则在data中定义,它和业务是相关的。

这种情况在C# / JAVA 中做反序列化时一般用泛型表示,如:

class MyAPI
{
  public int x;
  public int y;
}

class ApiResponse
{
  public int code;
  public string message;
  public T data;
}

ApiResponse resp = JsonConvert.DeserializeObject>(...);

C++的处理方案由于RTTI(Runtime Type Information运行时类型信息)并不包含属性名等信息,无法通过反射找到类/结构的属性,因此无法实现C# / JAVA那种看起来比较优雅的写法。

github上解决这个问题的项目很多,但它们许多要求使用c++17或20以上的版本,由于我的项目限制只能使用c++14,因此我只看c++11或14的解决方案。

这些项目的解决方案大体有两类:

1.提供更友好的读写属性的方法,比如通过["..."]["..."]层层嵌套,让用户更容易读写属性,但是它并没有完成在对象/结构实体上的映射和赋值,相当于这个工作得用户来做,因此它更像是只提供了一些序列化/反序列化的辅助方法,而不是完整的json和对象转化的解决方案,标星超过3万的https://github.com/nlohmann/json就是这种情况;

2.允许用户用通常的写法去定义类/结构,再通过宏去标识需要和json做映射的属性,完成类似其他语言的反射机制,当然它是在编译期实现属性定位而不是其他语言的运行期。

方案2在使用成本上相较方案1要小很多,也更符合主流方案的习惯。我看了几个项目,最后选了json_struct(https://github.com/jorgen/json_struct),它的解决方案是:

对于一个json串:

{
  "One" : 1,
  "Two" : "two",
  "Three" : 3.333
}

定义一个类:

struct JsonObject
{
  int One;
  std::string Two;
  double Three;
  
  JS_OBJ(One, Two, Three);
};

JS_OBJ是用于解决反射问题的宏,也可以定义在类外面:

struct JsonObject
{
  int One;
  std::string Two;
  double Three;
};
JS_OBJ_EXT(JsonObject, One, Two, Three);

反序列化:

JS::ParseContext context(json_data);
JsonObject obj;
context.parseTo(obj);

由此可见,除了所有方案都要写的类/结构定义之外,为反序列化要做的也就只写几行代码而已,相对方案1是简单了很多的,与C# / JAVA的解决方案相当。

回到最初的需求:

{
  "code": 0,
  "message": "",
  "data": {...}
}

这个结构是嵌套的,无法使用类似于C# / JAVA 的写法:

class MyAPI
{
  public int x;
  public int y;
}

class ApiResponse
{
  public int code;
  public string message;
  public T data;
}

ApiResponse resp = JsonConvert.DeserializeObject>(...);

json_struct的文档和examples中也没有提供明确的案例可以参考。

我试了一下,可以这样来定义:

struct ApiResponse
{
  int code;
  string message;

  JS_OBJ(code, message);
}

struct MyApiData
{
  int x;
  int y;

  JS_OBJ(x, y);
}

struct MyAPI : public ApiResponse
{
  MyApiData data;

  JS_OBJ(data);
}

然后这样解析就可以了:

JS::ParseContext context(...);
MyAPI myApi;
context.parseTo(myApi);

上述写法,相较C# / JAVA,C# / JAVA是用ApiResponse来包MyAPI(泛型传参),C++是用MyAPI来包ApiResponse(子类继承),并将子属性data从MyAPI拆出来,相当于多定义了一级,从语法上似乎稍微累赘一点,但整体结构还是清晰直观的。

展开阅读全文

页面更新:2024-05-13

标签:方案   嵌套   写法   反射   属性   定义   解决方案   版本   结构   项目   用户

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top