V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
liaotonglang
V2EX  ›  分享创造

参考 protobuf 的想法,写了个 C 语言的 JSON 处理库,可以方便完成 C struct 到 JSON 之间的转换

  •  
  •   liaotonglang · 2022-03-24 16:46:16 +08:00 · 2263 次点击
    这是一个创建于 735 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近用 C 语言处理 JSON ,感觉很麻烦,就写了这个可以生成 JSON 处理代码的程序。

    https://github.com/zltl/json-gen-c

    cover

    json-gen-c 通过读取结构体 (struct) 定义文件,生成对应的 JSON 处理代码。这些代码包括 struct 结构体定义,结构体初始化和清理函数,结构体 JSON 编码和解码函数。

    编译

    使用下列命令构建并安装:

    make
    sudo make install
    

    使用示范

    1. 编辑 struct.json-gen-c 文件,内容如下
    // 结构体定义文件
    
    // 定义结构体 A
    struct A {
        // 定义成员变量
        int int_val1;
        int int_val2;
        long long_val;
        double double_val;
        float float_val;
        sstr_t sstr_val;
        // 还可以定义数组
        int int_val_array[];
        // 定义别的结构体类型的变量
        B b_val;
    };
    
    // 定义结构体 B
    struct B {
        int id;
    };
    
    1. 构建 json.gen.cjson.gen.h

    使用下列命令创建 json 处理代码:

    json-gen-c -in struct.json-gen-c -out .
    

    命令创建了 json.gen.cjson.gen.h 文件,其中包含了 json 处理代码。同时创建了他们依赖的 sstr.hsstr.c,这是用于字符串处理的代码,程序中大量地方使用了他们。

    1. 使用 json 处理代码 例如要解析 JSON 字符串到结构体 A
    // const char *p_str = "{this is a json string}";
    // sstr_t json_str = sstr(pstr);
    
    struct A a;
    A_init(&a);
    json_unmarshal_A(json_str, &a); // 注意 json_str 是 sstr_t 类型的
    // ...
    A_clear(&a);
    

    要解析 JSON 字符串到结构体 A 的数组 A a[]:

    // const char *p_str = "this is a json string";
    // sstr_t json_str = sstr(pstr);
    
    struct A *a = NULL;
    int len = 0;
    json_unmarshal_array_A(&a, &len, json_str); // 注意 json_str 是 sstr_t 类型的
    // ...
    int i;
    for (i = 0; i < len; ++i) {
        A_clear(&a[i]);
    }
    free(a);
    

    要将结构体 A 序列化成 JSON 字符串:

    struct A a;
    A_init(&a);
    // set values to a ...
    // ...
    sstr_t json_str = sstr_new();
    json_marshal_A(&a, json_str);
    
    printf("marshal a to json> %s\n", sstr_cstr(json_str));
    
    sstr_free(json_str);
    A_clear(&a);
    

    要将结构体数组 A a[] 序列化成 JSON 字符串:

    struct A a[3];
    for (i = 0; i < 3; ++i) {
        A_init(&a[i]);
        // set values to a[i] ...
    }
    
    sstr_t json_str = sstr_new();
    json_marshal_array_A(a, 3, json_str);
    
    printf("marshal a[] to json> %s\n", sstr_cstr(json_str));
    
    for (i = 0; i < 3; ++i) {
        A_clear(&a[i]);
    }
    

    结构定义文件格式

    结构定义格式如下:

    struct <struct_name> {
        <field_type> <field_name> []?;
        <field_type> <field_name> []?;
        ...
    };
    

    <field_type> 可以是下列内容之一:

    • int
    • long
    • float
    • double
    • sstr_t
    • a struct name

    如果是成员变量是一个数组,就在变量名后面加 []。name.

    API

    // initialize a struct
    // always return 0
    int <struct_name>_init(struct <struct_name> *obj);
    
    // uninitialize a struct
    // always return 0
    int <struct_name>_clear(struct <struct_name> *obj);
    
    // marshal a struct to json string.
    // return 0 if success.
    int json_marshal_<struct_name>(struct <struct_name>*obj, sstr_t out);
    
    // marshal an array of struct to json string.
    // return 0 if success.
    int json_marshal_array<struct_name>(struct <struct_name>*obj, int len, sstr_t out);
    
    // unmarshal a json string to a struct.
    // return 0 if success.
    int json_unmarshal_<struct_name>(sstr_t in, struct <struct_name>*obj);
    
    // unmarshal a json string to array of struct
    // return 0 if success.
    int json_unmarshal_<struct_name>(sstr_t in, struct <struct_name>**obj, int *len);
    
    10 条回复    2022-03-24 22:10:31 +08:00
    0o0O0o0O0o
        1
    0o0O0o0O0o  
       2022-03-24 17:01:25 +08:00 via iPhone
    https://app.quicktype.io/

    更习惯这种依据 json 生成定义和代码的流程
    darkbfly666
        2
    darkbfly666  
       2022-03-24 17:10:48 +08:00
    darkbfly666
        3
    darkbfly666  
       2022-03-24 17:11:27 +08:00
    我也再这里推荐一下 这个库 挺好用的
    3dwelcome
        4
    3dwelcome  
       2022-03-24 17:13:22 +08:00
    json 是可变类型,怎么能把结构写死呢。

    正常都是把 json 转换成一些可变对象,比如 C++里面的 VARTYPE 对象,一个类型包含了 20 个子类型。

    然后根据 json key 再解析具体数据,结构写死一点容错性都没有了。json 毕竟不是 proto ,不可能保证 100%结构正确的。
    liaotonglang
        5
    liaotonglang  
    OP
       2022-03-24 17:14:51 +08:00
    @0o0O0o0O0o quicktype 确实方便啊,要是他有纯 C 的版本,我也不至于那么累。有空的话我给他加一个纯 C 语言依赖 cJSON 的选项。
    3dwelcome
        6
    3dwelcome  
       2022-03-24 17:17:06 +08:00
    举个例子,json 节点下有一堆 array ,有时候为空,有时候为 A 结构,有时候为 B 结构。

    具体类型最终是服务器来确定,你这样变成了客户端来确定结构了。
    liaotonglang
        7
    liaotonglang  
    OP
       2022-03-24 17:20:56 +08:00
    @3dwelcome 只是提供一个选项,强类型的 json 处理也有一定的使用场景。比如 golang 的 encoding/json 包,或者 rust 的 serde_json ,他们都提供了强类型的选项,同时支持将 json 解码为无类型结构。
    liaotonglang
        8
    liaotonglang  
    OP
       2022-03-24 17:21:37 +08:00
    @3dwelcome 可惜在 C 语言里,我没找到与他们相对应的方便的语法,就只有强类型结构了
    liaotonglang
        9
    liaotonglang  
    OP
       2022-03-24 17:25:17 +08:00
    @darkbfly666 xpack 看起来很方便,但语法看起来挺奇怪的,我怕会引起同事们反复的提问。如果做 C++的项目我还是会用 nlohmann::json 或者直接用 @0o0O0o0O0o 提到的 quicktype 。
    gyf304
        10
    gyf304  
       2022-03-24 22:10:31 +08:00 via iPhone
    写过个类似的 https://v2ex.com/t/753390
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1045 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:17 · PVG 03:17 · LAX 12:17 · JFK 15:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.