AIGCJson 库解析行为与异常处理指南

AIGCJson 库解析行为与异常处理指南

目录

  1. 概述
  2. 基本工作原理
  3. 字段解析规则
  4. [null 值处理](#null 值处理)
  5. 解析失败行为
  6. 最佳实践
  7. 常见问题

概述

AIGCJson 是一个基于 RapidJSON 的 C++ JSON 序列化/反序列化库,通过宏定义简化结构体与 JSON 之间的转换。

核心特性

  • 支持基本类型:int, bool, float, double, string
  • 支持容器类型:vector, list, map, set
  • 支持嵌套结构体
  • 通过 AIGC_JSON_HELPER 宏自动生成序列化代码

基本工作原理

1. 结构体定义

cpp 复制代码
struct MyStruct {
    std::string name;
    int age;
    bool is_active;
    
    // 使用宏注册字段,用于序列化/反序列化
    AIGC_JSON_HELPER(name, age, is_active);
};

2. 整体解析流程图







开始: JsonToObject调用
解析JSON字符串
JSON解析成功?
返回false: JSON格式错误
创建JsonHelperPrivate对象
调用结构体的AIGCJsonToObject
获取字段名称列表
开始字段遍历: index=0
还有字段未处理?
返回true: 解析成功
处理当前字段
字段解析成功?
返回false: 停止解析
index++

3. 解析流程说明

当调用 JsonToObject 解析 JSON 时:

  1. JSON 解析 :将 JSON 字符串解析为 RapidJSON 的 Value 对象
  2. 字段遍历 :按照 AIGC_JSON_HELPER 中声明的顺序逐个处理字段
  3. 类型检查:对每个字段进行类型匹配检查
  4. 值设置:类型匹配成功则设置值,失败则记录错误并停止

4. 解析顺序

字段按照 AIGC_JSON_HELPER 中声明的顺序从左到右依次解析。

示例

复制代码
AIGC_JSON_HELPER(name, age, is_active)
         ↓         ↓        ↓
      第1个      第2个     第3个

字段解析规则

规则 1:单个字段解析流程图











开始处理字段
JSON对象是否为null?
返回true: 跳过设置
JSON是否为对象类型?
返回false: 类型错误
字段是否存在?
有默认值?
使用默认值
保持原值
返回true: 继续下一个字段
调用JsonToObject解析字段值
解析成功?
记录错误信息
返回false: 停止解析
设置字段值
返回true: 继续下一个字段

规则 2:字段存在性检查

JSON 字段状态 行为 返回值
字段存在且类型匹配 解析并设置值 true
字段存在但类型不匹配 解析失败,记录错误 false
字段不存在 使用默认值(如果有),否则保持原值 true

规则 2:字段缺失处理

当 JSON 中某个字段不存在时:

  • 如果字段有默认值 (通过 AIGC_JSON_HELPER_DEFAULT 设置),使用默认值
  • 如果字段没有默认值,保持 C++ 结构体的初始化值
  • 不会导致解析失败,继续处理后续字段

示例

cpp 复制代码
struct User {
    std::string name;
    int age = 18;  // 默认值
    bool is_vip = false;  // 默认值
    AIGC_JSON_HELPER(name, age, is_vip);
};

// JSON: {"name": "Alice"}
// 解析结果:
// - name = "Alice"  ✅
// - age = 18        ✅ (使用默认值)
// - is_vip = false  ✅ (使用默认值)

null 值处理

null 值处理流程图

string
bool/int/float/double
结构体
遇到null值
字段类型是什么?
特殊处理
设置为空字符串
返回true: 解析成功
类型检查
IsBool/IsInt/IsNumber返回false
记录错误信息
返回false: 解析失败
递归处理
遇到null直接返回
所有字段保持默认值
返回true: 解析成功

不同类型对 null 的处理

数据类型 null 值行为 结果
std::string 特殊处理,解析成功 设置为空字符串 ""
bool 类型检查失败 解析失败,返回 false
int, float, double 类型检查失败 解析失败,返回 false
自定义结构体 递归处理,遇到 null 直接返回 解析成功,所有字段保持默认值

null 值处理对比图

复制代码
┌─────────────────────────────────────────────────────────────┐
│ JSON: {"name": null, "age": null, "is_vip": null}          │
└─────────────────────────────────────────────────────────────┘
                            ↓
        ┌───────────────────┼───────────────────┐
        ↓                   ↓                   ↓
    string类型          int类型            bool类型
        ↓                   ↓                   ↓
   特殊处理 ✅         类型检查 ❌         类型检查 ❌
        ↓                   ↓                   ↓
   name = ""          age解析失败          is_vip解析失败
   继续解析              停止解析              停止解析

结果

  • string 类型:解析成功,继续处理后续字段
  • int/bool 类型:解析失败,立即停止,后续字段不处理

string 类型的特殊处理

std::string 是唯一对 null 有特殊处理的基本类型:

cpp 复制代码
bool JsonToObject(std::string &obj, rapidjson::Value &jsonValue)
{
    obj = "";
    if (jsonValue.IsNull())
        return true;  // ✅ null 时成功解析为空字符串
    // ...
}

示例

json 复制代码
{
    "name": null,
    "age": 25
}

解析结果:

  • name = ""(空字符串,解析成功)
  • age = 25(正常解析)

基本类型的 null 处理

对于 bool, int, float, double 等基本类型,null 会导致解析失败:

cpp 复制代码
bool JsonToObject(bool &obj, rapidjson::Value &jsonValue)
{
    if (!jsonValue.IsBool())  // null 时 IsBool() 返回 false
    {
        m_message = "json-value is ... but object is bool.";
        return false;  // ❌ 解析失败
    }
    // ...
}

示例

json 复制代码
{
    "is_active": null,
    "name": "Test"
}

解析结果:

  • is_active:解析失败,保持默认值(如 false
  • name不会继续解析(因为前一个字段失败)

结构体类型的 null 处理

当结构体字段为 null 时:

cpp 复制代码
// 在 SetMembers 中
if (jsonValue.IsNull())
    return true;  // 直接返回,不设置任何值

示例

cpp 复制代码
struct Address {
    std::string city;
    std::string street;
    AIGC_JSON_HELPER(city, street);
};

struct User {
    std::string name;
    Address address;  // 结构体字段
    AIGC_JSON_HELPER(name, address);
};
json 复制代码
{
    "name": "Alice",
    "address": null
}

解析结果:

  • name = "Alice"
  • address.city = ""(默认值)
  • address.street = ""(默认值)
  • 整体解析成功 ✅

解析失败行为

核心机制:短路停止流程图









开始解析结构体
处理字段1: name
字段1解析成功?
❌ 立即停止
✅ 字段1已设置值
处理字段2: age
字段2解析成功?
❌ 立即停止
✅ 字段2已设置值
处理字段3: is_vip
字段3解析成功?
❌ 立即停止
✅ 字段3已设置值
处理字段4: email
字段4解析成功?
❌ 立即停止
✅ 所有字段解析完成
返回true: 解析成功
返回false: 解析失败

后续字段保持默认值

核心机制:短路停止

重要特性 :当遇到第一个解析失败的字段时,立即停止 ,后续字段不会继续解析

代码逻辑

cpp 复制代码
template <typename TYPE, typename... TYPES>
bool SetMembers(..., TYPE &arg, TYPES &...args)
{
    // 处理第一个字段
    if (!SetMembers(..., arg))
        return false;  // ❌ 失败则立即返回,不继续
    
    // 只有成功才继续处理后续字段
    return SetMembers(..., args...);
}

可视化示例:解析过程对比

场景 A:所有字段成功解析
复制代码
JSON: {"name": "Alice", "age": 25, "is_vip": true, "email": "alice@example.com"}

解析过程:
┌─────────────────────────────────────────────────────────┐
│ 字段1: name = "Alice"        ✅ 成功 → 继续            │
│ 字段2: age = 25              ✅ 成功 → 继续            │
│ 字段3: is_vip = true         ✅ 成功 → 继续            │
│ 字段4: email = "alice@..."   ✅ 成功 → 完成            │
└─────────────────────────────────────────────────────────┘
结果: ✅ 全部成功,所有字段都有值
场景 B:第二个字段失败
复制代码
JSON: {"name": "Alice", "age": null, "is_vip": true, "email": "alice@example.com"}

解析过程:
┌─────────────────────────────────────────────────────────┐
│ 字段1: name = "Alice"        ✅ 成功 → 继续            │
│ 字段2: age = null            ❌ 失败 → 立即停止        │
│ 字段3: is_vip                 ⏸️  未处理(保持默认值)  │
│ 字段4: email                  ⏸️  未处理(保持默认值)  │
└─────────────────────────────────────────────────────────┘
结果: ❌ 解析失败
      - name = "Alice" (已解析)
      - age = 0 (默认值,未解析)
      - is_vip = false (默认值,未解析)
      - email = "" (默认值,未解析)

实际影响

场景 行为 结果
字段 1 解析失败 立即停止 字段 1 保持默认值,字段 2-N 保持默认值
字段 2 解析失败 立即停止 字段 1 有值,字段 2 保持默认值,字段 3-N 保持默认值
所有字段成功 全部处理 所有字段都有值

示例场景

cpp 复制代码
struct User {
    std::string name;
    int age;
    bool is_vip;
    std::string email;
    AIGC_JSON_HELPER(name, age, is_vip, email);
};
场景 1:第二个字段失败
json 复制代码
{
    "name": "Alice",
    "age": null,  // ❌ int 类型不能是 null
    "is_vip": true,
    "email": "alice@example.com"
}

解析过程可视化

复制代码
步骤1: 处理 name 字段
┌─────────────────────────────────────┐
│ JSON字段: "name": "Alice"          │
│ 类型检查: string ✅                 │
│ 设置值: name = "Alice"              │
│ 结果: ✅ 成功,继续下一个字段        │
└─────────────────────────────────────┘

步骤2: 处理 age 字段
┌─────────────────────────────────────┐
│ JSON字段: "age": null               │
│ 类型检查: int ❌ (null不是int类型)   │
│ 设置值: age = 0 (保持默认值)         │
│ 结果: ❌ 失败,立即停止              │
└─────────────────────────────────────┘

步骤3-4: 不再处理
┌─────────────────────────────────────┐
│ is_vip: 未处理,保持默认值 false    │
│ email: 未处理,保持默认值 ""        │
└─────────────────────────────────────┘

解析结果

  • name = "Alice" ✅(已解析)
  • age = 0(默认值,解析失败后保持)
  • is_vip = false(默认值,未解析
  • email = ""(默认值,未解析

解析函数返回false(失败)

场景 2:所有字段成功
json 复制代码
{
    "name": "Alice",
    "age": 25,
    "is_vip": true,
    "email": "alice@example.com"
}

解析过程可视化

复制代码
┌─────────────────────────────────────────────────────────┐
│ 字段1: name                                             │
│   JSON值: "Alice"                                       │
│   类型: string ✅                                        │
│   结果: name = "Alice" → 继续                           │
├─────────────────────────────────────────────────────────┤
│ 字段2: age                                              │
│   JSON值: 25                                            │
│   类型: int ✅                                           │
│   结果: age = 25 → 继续                                 │
├─────────────────────────────────────────────────────────┤
│ 字段3: is_vip                                           │
│   JSON值: true                                          │
│   类型: bool ✅                                          │
│   结果: is_vip = true → 继续                            │
├─────────────────────────────────────────────────────────┤
│ 字段4: email                                            │
│   JSON值: "alice@example.com"                          │
│   类型: string ✅                                        │
│   结果: email = "alice@example.com" → 完成             │
└─────────────────────────────────────────────────────────┘
✅ 所有字段解析成功

解析结果

  • 所有字段都有值 ✅
  • 解析函数返回:true(成功)
场景 3:字段缺失(不是 null)
json 复制代码
{
    "name": "Alice"
    // 其他字段缺失
}

解析过程可视化

复制代码
┌─────────────────────────────────────────────────────────┐
│ 字段1: name                                             │
│   JSON值: "Alice"                                       │
│   存在: ✅                                               │
│   结果: name = "Alice" → 继续                           │
├─────────────────────────────────────────────────────────┤
│ 字段2: age                                              │
│   JSON值: 不存在                                        │
│   默认值: 0 ✅                                            │
│   结果: age = 0 (使用默认值) → 继续                     │
├─────────────────────────────────────────────────────────┤
│ 字段3: is_vip                                           │
│   JSON值: 不存在                                        │
│   默认值: false ✅                                       │
│   结果: is_vip = false (使用默认值) → 继续              │
├─────────────────────────────────────────────────────────┤
│ 字段4: email                                            │
│   JSON值: 不存在                                        │
│   默认值: "" ✅                                          │
│   结果: email = "" (使用默认值) → 完成                 │
└─────────────────────────────────────────────────────────┘
✅ 所有字段处理完成(字段缺失不算错误)

解析结果

  • name = "Alice"
  • age = 0(默认值,字段缺失不影响)
  • is_vip = false(默认值,字段缺失不影响)
  • email = ""(默认值,字段缺失不影响)

解析函数返回true(成功,字段缺失不算错误)


最佳实践

1. 设置合理的默认值

为所有字段设置合理的默认值,避免解析失败时出现意外值:

cpp 复制代码
struct User {
    std::string name;
    int age = 0;              // ✅ 设置默认值
    bool is_vip = false;      // ✅ 设置默认值
    std::string email;
    
    AIGC_JSON_HELPER(name, age, is_vip, email);
};

2. 服务端避免返回 null

对于基本类型(bool, int, float 等),服务端应避免返回 null

不推荐

json 复制代码
{
    "is_active": null,
    "age": null
}

推荐

json 复制代码
{
    "is_active": false,  // 返回实际值
    "age": 0            // 或省略该字段
}

3. 检查解析结果

始终检查 JsonToObject 的返回值:

cpp 复制代码
User user;
std::string jsonStr = "...";

std::string errorMsg;
if (!aigc::Json::JsonToObject(user, jsonStr, {}, &errorMsg)) {
    // 处理解析失败
    LOG_ERROR("解析失败: %s", errorMsg.c_str());
    // 使用默认值或返回错误
    return false;
}

// 解析成功,使用 user 对象

4. 使用可选字段模式

对于可能为 null 的字段,考虑使用 std::optional(需要自定义处理)或通过业务逻辑判断:

cpp 复制代码
struct User {
    std::string name;
    std::string email;
    
    // 通过检查 email 是否为空来判断是否为 null
    bool hasEmail() const { return !email.empty(); }
    
    AIGC_JSON_HELPER(name, email);
};

5. 字段顺序考虑

关键字段 放在前面,可选字段放在后面:

cpp 复制代码
struct User {
    std::string id;        // ✅ 关键字段在前
    std::string name;      // ✅ 关键字段在前
    int age = 0;           // 可选字段
    bool is_vip = false;   // 可选字段
    
    AIGC_JSON_HELPER(id, name, age, is_vip);
};

这样即使后面的字段解析失败,关键字段也能正确解析。


常见问题

Q1: 为什么 string 类型的 null 能成功解析,但 bool 不行?

A : 这是库的设计决策。string 类型对 null 有特殊处理,会解析为空字符串,而其他基本类型(bool, int 等)严格检查类型,null 不符合类型要求。

Q2: 解析失败后,已解析的字段值会保留吗?

A : 会保留。解析是顺序进行的,已成功解析的字段值会被设置,失败时停止,后续字段保持默认值。

Q3: 字段缺失和字段为 null 有什么区别?

A:

  • 字段缺失:不会导致解析失败,使用默认值,继续处理后续字段
  • 字段为 null
    • string 类型:解析为空字符串,继续处理
    • 其他基本类型:解析失败,停止处理
    • 结构体类型:解析成功(所有字段保持默认值),继续处理

Q4: 如何判断字段是缺失还是 null?

A : 对于 string 类型,无法区分(都是空字符串)。对于其他类型,如果解析失败,可能是 null 或类型不匹配。建议在服务端统一处理,避免返回 null。

Q5: 解析失败时如何获取错误信息?

A : 使用 JsonToObjectmessage 参数:

cpp 复制代码
std::string errorMsg;
if (!aigc::Json::JsonToObject(obj, jsonStr, {}, &errorMsg)) {
    // errorMsg 包含详细的错误信息
    std::cout << "错误: " << errorMsg << std::endl;
}

Q6: 能否让解析失败时继续处理后续字段?

A: 当前版本的 AIGCJson 不支持。如果需要这个功能,需要修改库源码或使用其他 JSON 库。


总结

核心要点速查表

要点 说明 影响
解析顺序 按照 AIGC_JSON_HELPER 声明的顺序从左到右解析 关键字段应放在前面
短路停止 遇到第一个解析失败的字段立即停止 后续字段不会处理
null 处理 只有 string 类型对 null 有特殊处理,其他类型会失败 服务端应避免返回 null
字段缺失 不算错误,使用默认值并继续处理 字段缺失是安全的
默认值重要性 为所有字段设置合理的默认值 避免解析失败时出现意外值

决策流程图





string
其他基本类型
结构体




开始解析JSON
字段存在?
使用默认值
继续下一个字段
字段值为null?
类型匹配?
字段类型?
设置为空字符串
解析失败
所有字段保持默认值
停止解析
设置字段值
还有字段?
解析完成
返回false
返回true

建议

  • ✅ 为所有字段设置默认值
  • ✅ 服务端避免返回 null(特别是基本类型)
  • ✅ 检查解析结果并处理错误
  • ✅ 将关键字段放在前面
  • ✅ 使用错误消息参数获取详细错误信息

快速参考卡片

复制代码
┌─────────────────────────────────────────────────────────┐
│  AIGCJson 解析行为快速参考                               │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ✅ 字段缺失     → 使用默认值,继续解析                  │
│  ✅ string null  → 解析为空字符串,继续解析              │
│  ❌ bool null    → 解析失败,立即停止                    │
│  ❌ int null     → 解析失败,立即停止                    │
│  ✅ 结构体 null  → 所有字段默认值,继续解析             │
│  ❌ 类型不匹配   → 解析失败,立即停止                    │
│                                                         │
│  关键原则:遇到第一个失败立即停止,后续字段不处理       │
│                                                         │
└─────────────────────────────────────────────────────────┘

文档创建时间:2026-01-10
基于 AIGCJson 库源码分析整理


相关推荐
liu****8 小时前
4.Qt窗口开发全解析:菜单栏、工具栏、状态栏及对话框实战
数据库·c++·qt·系统架构
近津薪荼8 小时前
优选算法——双指针6(单调性)
c++·学习·算法
helloworldandy8 小时前
高性能图像处理库
开发语言·c++·算法
2401_836563188 小时前
C++中的枚举类高级用法
开发语言·c++·算法
EmbedLinX9 小时前
C++ 面向对象
开发语言·c++
weixin_445402309 小时前
C++中的命令模式变体
开发语言·c++·算法
季明洵9 小时前
C语言实现顺序表
数据结构·算法·c·顺序表
Hgfdsaqwr9 小时前
实时控制系统优化
开发语言·c++·算法
CSDN_RTKLIB9 小时前
Visual Studio不改变文件编码情况下解决C2001
c++·ide·visual studio
D_evil__9 小时前
【Effective Modern C++】第三章 转向现代C++:15. 尽可能使用constexpr
c++