65.9K
CodeProject 正在变化。 阅读更多。
Home

ZetJsonCpp - 从/到 JSON 字符串或文件进行反序列化/序列化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (18投票s)

2014年8月8日

MIT

5分钟阅读

viewsIcon

68981

downloadIcon

262

一个解析 Json 字符串或文件并绑定到 C 结构的库

也可以从 Github 下载。

引言

如今,JSON 结构已成为 Web 应用程序中最常用的数据交换方式。这是因为 JSON 是最简单、最易于人类阅读的格式,因此使用起来更友好。本文旨在介绍如何在 C++ 中使用 zetjsoncpp 库从/到 JSON 字符串或文件进行反序列化/序列化。

1. 反序列化

使用 zetjsoncpp::deserialize 来序列化 JSON 变量。Zet JsonCpp 支持反序列化以下类型:

  • 布尔值
  • 布尔值向量
  • 布尔值映射
  • 数字
  • 数字向量
  • 数字映射
  • 字符串
  • 字符串向量
  • 字符串映射
  • 对象
  • 对象向量
  • 对象映射

2.1 布尔值

反序列化 JSON 布尔值是通过 JsonVarBoolean 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarBoolean<>>("true");

2.2 布尔值向量

JSON 布尔值向量可能是这样的:

[true,false,true]

反序列化 JSON 布尔值向量是通过 JsonVarVectorBoolean 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarVectorBoolean<>>(
"["
   "true"
   ",false"
   ",true"
"]");

2.3 布尔值映射

JSON 布尔值映射可能是这样的:

{
   "id1":true
  ,"id2":false
  ,"id3":true
}

反序列化 JSON 布尔值映射是通过 JsonVarMapBoolean 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapBoolean<>>(
"{"
   "\"id1\":true"
   ",\"id2\":false"
   ",\"id3\":true"
"}");

2.4 数字

JSON 数字可能是这样的:

  • -1
  • 2.5
  • 3.7e+2

反序列化 JSON 数字是通过 JsonVarNumber 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarNumber<>>("1");

2.5 数字向量

JSON 数字向量可能是这样的:

[1,3.7e+2,-3]

反序列化 JSON 数字向量是通过 JsonVarVectorNumber 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarVectorNumber<>>(
"["
   "1"
   ",3.7e+2"
   ",-3"
"]");

2.6 数字映射

JSON 数字映射可能是这样的:

{ 
  "id1":1
 ,"id2":3.7e+2
 ,"id3":-3
}

反序列化 JSON 数字映射是通过 JsonVarMapNumber 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapNumber<>>(
"{"
   "\"id1\":1"
   ",\"id2\":3.7e+2"
   ",\"id3\":-3"
"}");

2.7 字符串

反序列化 JSON 字符串是通过 JsonVarString 完成的,如下所示:

zetjsoncpp::deserialize<zetjsoncpp::JsonVarString<>>("\"my_string\"")

2.8 字符串向量

JSON 字符串向量可能是这样的:

["string_1","string_2","string_3"]

反序列化 字符串向量是通过 JsonVarVectorString 完成的,如下所示:

zetjsoncpp::deserialize<JsonVarVectorString<>>(
"["
    "\"string_1\""
    ",\"string_2\""
    ",\"string_3\""
"]");

2.9 字符串映射

JSON 字符串映射可能是这样的:

{ 
   "id1":"string_1"
   ,"id2":"string_2"
   ,"id3":"string_3" 
}

反序列化 字符串映射是通过 JsonVarMapString 完成的,如下所示:

JsonVarMapString<> *m3=zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapString<>>(
"{"
    "\"id1\":\"string_1\""
    ",\"id2\":\"string_2\""
    ",\"id3\":\"string_3\""
"}");

2.10 对象

到目前为止,我们已经了解了如何序列化易于理解的原始类型和结构化类型。现在,我们介绍一种需要一些设置的 JSON 对象反序列化方法。JSON 对象类似于 JSON 映射,但其值的类型不同。

JSON 对象可能是这样的:

{
  "encoding":"UTF-8"
 ,"length":1000
 ,"use_space":false
}
列表 2.1

以列表 2.1 的示例为例,在 zetjsoncpp 中,使用 C 结构来定义 JSON 对象,如下所示:

typedef struct{
    zetjsoncpp::JsonVarString<ZJ_CONST_CHAR("encoding")>    encoding;
    zetjsoncpp::JsonVarNumber<ZJ_CONST_CHAR("length")>      length;     
    zetjsoncpp::JsonVarBoolean<ZJ_CONST_CHAR("use_space")>  use_space; 
}JsonSample;
列表 2.2

注意:

您可能已经注意到 ZJ_CONST_CHAR(s)。这是一个技巧,用于逐个字符地通过可变参数模板传递字符串字面量,以便模板不接受(例如 const char *)作为参数。

最后,反序列化 JSON 对象是通过 JsonVarObject 完成的,它传递了在列表 2.2 中看到的用于反序列化的结构类型。

auto json_object=zetjsoncpp::deserialize<zetjsoncpp::JsonVarObject<JsonSample>>(
"{"
   "\"encoding\":\"UTF-8\""
   ",\"length\":1000"
   ",\"use_space\":false"         
"}");

如果任何变量未被反序列化,因为它们不存在于字符串/文件中,或者它们与 JSON 属性名与 C++ 结构中定义的属性名不匹配,则必须使用 isDeserialized() 来检查变量是否已被反序列化。

例如

if(json_object->encoding.isDeserialized()){

// value deserialized ok. do something... 

}
列表 3.4

默认情况下,任何未反序列化的变量,字符串将被设置为空,数字和布尔值将分别设置为 0false

2.11 对象向量

JSON 对象向量可能如下所示:

[{ 
    "encoding":"UTF-8" 
    ,"length":1000 
    ,"use_space":false 
 },{   
    "encoding":"ANSII"
   ,"length":1500   
   ,"use_space":true 
}]

反序列化对象向量是通过 JsonVarVectorObject 完成的,它传递了在列表 2.2 中看到的用于反序列化的结构类型。

zetjsoncpp::deserialize<zetjsoncpp::JsonVarVectorObject<JsonSample>>(
"[{"  
   "\"encoding\":\"UTF-8\""  
   ",\"length\":1000"   
   ",\"use_space\":false" 
  "},{"  
    "\"encoding\":\"ANSII\""  
    ",\"length\":1500"  
    ",\"use_space\":true" 
"}]");

2.12 对象映射

JSON 对象映射可能是这样的:

{
  "id1":{ 
     "encoding":"UTF-8" 
    ,"length":1000 
    ,"use_space":false
  }
  ,"id2":{
     "encoding":"ANSII"
     ,"length":1500
     ,"use_space":true
  }
}

反序列化对象映射是通过 JsonVarMapObject 完成的,它传递了在列表 2.2 中看到的用于反序列化的结构类型。

zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapObject<JsonSample>>(
"{"
  "\"id1\":{"
     "\"encoding\":\"UTF-8\""  
     ",\"length\":1000"    
     ",\"use_space\":false" 
  "}"
  ",\"id2\":{"
      "\"encoding\":\"ANSII\""
      ",\"length\":1500"  
      ",\"use_space\":true" 
  "}"
"}");

2. 序列化

使用 zetjsoncpp::serialize 来序列化 JSON 变量。

例如

// parse json var number
auto json_number=zetjsoncpp::deserialize<zetjsoncpp::JsonVarNumber<>>("2");

// change it by 3.5
json_number=3.5;

std::cout << zetjsoncpp::serialize(json_var); << std::enl;

// it outputs 3.5

3. 示例

在本节中,我们将看到一个反序列化第 2 节中提到的大部分类型的示例。假设我们有一个名为 sample.json 的文件,其内容如下:

// Configuration options
{
    // Default encoding for text
    "encoding" : "UTF-8",
    "number": 3.34E-5,
    // Plug-ins loaded at start-up
    "plug-ins" : [
        "python",
        "c++",
        "ruby"
      ],
        
    // Tab indent size
    "indent" : { "length" : 3, "use_space": true },
    
    // set of languages
    "languages":[{
    
        "code" : "en",
        "general_texts": {
            "general.hello_word":"Hello world!"
            ,"general.yes":"Yes"
            ,"general.no":"No"
        }
        
    },{
        "code" : "es",
        "general_texts": {
            "general.hello_word":"Hola mundo!"
            ,"general.yes":"Si"
            ,"general.no":"No"
        }
    },{
        "code" : "zh-CN",
        "general_texts": {
            "general.hello_word":"你好词"
            ,"general.yes":"是"
            ,"general.no":"没有"
        }
    }]
    // set of interpolators
    ,"interpolations":{
        "id_1":{
             "type":"material"
            ,"channels":"rgb"
            ,"data":[
             // r    g   b   t
             //---- --- --- ----
                0.0,1.0,0.0,1000
                ,0.0,0.0,0.0,0
            ]
        },"id_2":{
             "type":"transform"
            ,"channels":"xyz"
            ,"data":[
             // x    y   z   t
             //---- --- --- ----
                0.0,1.0,0.0,1000
                ,0.0,0.0,0.0,0
            ]
        }
    }
}
列表 3.1

我们可以将列表 3.1 中的 JSON 结构与以下带有 JSON 变量的 C 结构相关联:

#include "zetjsoncpp.h"

using zetjsoncpp::JsonVarNumber;
using zetjsoncpp::JsonVarBoolean;
using zetjsoncpp::JsonVarString;
using zetjsoncpp::JsonVarMapString;
using zetjsoncpp::JsonVarVectorNumber;
using zetjsoncpp::JsonVarVectorString;
using zetjsoncpp::JsonVarObject;
using zetjsoncpp::JsonVarVectorObject;
using zetjsoncpp::JsonVarMapObject;

typedef struct{
    // Number length
    JsonVarNumber<ZJ_CONST_CHAR("length")>    length;

    // Boolean use_space
    JsonVarBoolean<ZJ_CONST_CHAR("use_space")> use_space;
}Ident;

typedef struct{
    // String code
    JsonVarString<ZJ_CONST_CHAR("code")>    code;

    // Map of strings general_texts
    JsonVarMapString<ZJ_CONST_CHAR("general_texts")>    general_texts;
}Language;

typedef struct{
    // String type
    JsonVarString<ZJ_CONST_CHAR("type")>    type;

    // String channels
    JsonVarString<ZJ_CONST_CHAR("channels")>    channels;

    // Vector of numbers data
    JsonVarVectorNumber<ZJ_CONST_CHAR("data")>    data;
}Interpolation;

typedef struct
{
    // String encoding
    JsonVarString<ZJ_CONST_CHAR("encoding")>    encoding;

    // Number number
    JsonVarNumber<ZJ_CONST_CHAR("number")>    number;

    // Vector of strings plug-ins
    JsonVarVectorString<ZJ_CONST_CHAR("plug-ins")>    plugins;

    // Object indent
    JsonVarObject<Ident,ZJ_CONST_CHAR("indent")>    indent;

    // Object languages
    JsonVarVectorObject<Language,ZJ_CONST_CHAR("languages")>    languages;

    // Map of objects interpolations
    JsonVarMapObject<Interpolation,ZJ_CONST_CHAR("interpolations")>    interpolations;

}SampleJson;
列表 3.2

下面,您可以看到一个反序列化、加载的 JSON 对象的数据操作,最后序列化以反映所做更改的示例:

int main(int argc, char *argv[]){

    try{
        auto json_object=zetjsoncpp::deserialize_file<JsonVarObject<SampleJson>>("sample.json");

        // the values before modifications.
        std::cout << "---------------------------------------------------" << std::endl;
        std::cout << " Before modifications:"<< std::endl;
        std::cout << zetjsoncpp::serialize(json_object);

        // From here we can operate with loaded data in our program using c++ operators
        // put m_use_space to false...
        json_object->indent.use_space = false;

        // iterate of all plugins and replace with random strings...
        for(unsigned i = 0; i < json_object->plugins.size(); i++) {
            json_object->plugins[i] = 
                 "my_randomstring"+zetjsoncpp::zj_strutils::int_to_str(i+1);
        }

        // iterate of all interpolations and replace its data values...
        for(auto it_map = json_object->interpolations.begin(); 
            it_map != json_object->interpolations.end(); it_map++) {
            for(auto it = it_map->second->data.begin(); 
                it != it_map->second->data.end(); it++) {
                *it = rand();
            }
        }

        std::cout << "--------------------------------------------------" << std::endl;
        std::cout << " After modifications:"<< std::endl;
        std::cout << zetjsoncpp::serialize(json_object);

        // destroy json_object
        delete json_object;
  }catch(std::exception & ex){
    std::cerr << "Error:" << ex.what() << std::endl;
  }
}
列表 3.3

执行后,输出显示了在 **粗体** 中标记的更改之前和之后的序列化 JSON:

---------------------------------------------------
 Before modifications:
{
    "encoding":"UTF-8",
    "number":0.000033,
    "plug-ins":
    [
        "python","c++","ruby"
    ],
    "indent":
    {
        "length":3.000000,
        "use_space":true
    },
    "languages":
    [{
        "code":"en",
        "general_texts":
        {
            "general.hello_word":"Hello world!"
            ,"general.no":"No"
            ,"general.yes":"Yes"
        }
    },{
        "code":"es",
        "general_texts":
        {
            "general.hello_word":"Hola mundo!"
            ,"general.no":"No"
            ,"general.yes":"Si"
        }
    },{
        "code":"zh-CN",
        "general_texts":
        {
            "general.hello_word":"你好词"
            ,"general.no":"没有"
            ,"general.yes":"是"
        }
    }],
    "interpolations":
    {
        "id_1":{
            "type":"material",
            "channels":"rgb",
            "data":
            [
                0.000000,1.000000,0.000000,1000.000000,0.000000,0.000000,0.000000,0.000000
            ]
        }
        ,"id_2":{
            "type":"transform",
            "channels":"xyz",
            "data":
            [
                0.000000,1.000000,0.000000,1000.000000,0.000000,0.000000,0.000000,0.000000
            ]
        }
    }
}--------------------------------------------------
 After modifications:
{
    "encoding":"UTF-8",
    "number":0.000033,
    "plug-ins":
    [
        "my_randomstring1","my_randomstring2","my_randomstring3"
    ],
    "indent":
    {
        "length":3.000000,
        "use_space":false
    },
    "languages":
    [{
        "code":"en",
        "general_texts":
        {
            "general.hello_word":"Hello world!"
            ,"general.no":"No"
            ,"general.yes":"Yes"
        }
    },{
        "code":"es",
        "general_texts":
        {
            "general.hello_word":"Hola mundo!"
            ,"general.no":"No"
            ,"general.yes":"Si"
        }
    },{
        "code":"zh-CN",
        "general_texts":
        {
            "general.hello_word":"你好词"
            ,"general.no":"没有"
            ,"general.yes":"是"
        }
    }],
    "interpolations":
    {
        "id_1":{
            "type":"material",
            "channels":"rgb",
            "data":
            [
                41.000000,18467.000000,6334.000000,26500.000000,19169.000000,
                15724.000000,11478.000000,29358.000000
            ]
        }
        ,"id_2":{
            "type":"transform",
            "channels":"xyz",
            "data":
            [
                26962.000000,24464.000000,5705.000000,28145.000000,
                23281.000000,16827.000000,9961.000000,491.000000
            ]
        }
    }
}

4. 编译

要编译源代码,您需要 cmake 工具来创建其 Makefile 或 Visual Studio 解决方案,然后编译项目。

cmake -H. -Bbuild

编译后,除了生成 zetjsoncpp 库之外,还将分别生成名为 testtest_file 的可执行文件。名为 test 的可执行文件执行基本操作以检查其完整性,而 test_file 读取第 3 节中看到的 JSON 文件。

5. 结论

本文介绍了一种通过一行代码从字符串或文件中反序列化 JSON 内容的简单方法。结果对生产非常优化,并且因为它绑定在 C 结构中,用户可以轻松了解每个字段,因此易于处理。希望您发现此工具有用。

历史

2021-04-7 ZetJsonCpp 2.0.1

  • 修复当 jsonvar c++ 值与读取值不匹配时的错误

2021-02-15 ZetJsonCpp 2.0.0

  • 添加了对映射/字典元素的支持
  • 大规模的代码组织和清理
  • 支持带 bom 的 utf-8 文件
  • 支持反序列化任何 JSON 类型(例如,数字/字符串/布尔值/对象,元素向量或映射)

2018-05-15 ZetJsonCpp 1.3.0

  • 将版本历史记录审查为新格式(MAJOR.MINOR.PATCH
  • 错误/警告的行为通过回调函数 setFunctionOnErrorsetFunctionOnWarning 进行。如果未设置这些函数,则消息将使用 fprintfstderr)打印。
  • 添加了内存泄漏检测(仅限 GNU 工具链)。它需要 memmanager https://github.com/jespa007/memmanager 并在 cmake 上传递 -DMEMMANAGER 参数。
  • 修复了 MSVC 平台上的 compile test_json 共享库
  • 改进了 cmake 消息打印

2018-05-10 ZetJsonCpp 1.2.0

  • 项目通过 cmake 构建
  • MSVC 支持(v141 Tools 或 MSVC++ 2015)
  • 添加了 zetjsoncpp 命名空间
  • fastjsoncpp 重命名为 zetjsoncpp
  • 添加了检测数组类型的特性
  • 将 GPL3 许可证更改为 MIT

2015-08-29 ZetJsonCpp 1.1.0

  • 添加了支持数字科学计数法(例如 2.1E-10)的特性
  • 添加了检测属性组或属性组数组的特性
  • 修复了适用于 Windows 的回车换行符兼容性

2014-08-08 ZetJsonCpp 1.0.0

  • 首次发布
© . All rights reserved.