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 库源码分析整理


相关推荐
寻星探路9 小时前
【Python 全栈测开之路】Python 基础语法精讲(一):常量、变量与运算符
java·开发语言·c++·python·http·ai·c#
朔北之忘 Clancy9 小时前
2020 年 6 月青少年软编等考 C 语言一级真题解析
c语言·开发语言·c++·学习·算法·青少年编程·题解
御承扬9 小时前
鸿蒙原生系列之动画效果(帧动画)
c++·harmonyos·动画效果·ndk ui·鸿蒙原生
全栈前端老曹10 小时前
【包管理】read-pkg-up 快速上手教程 - 读取最近的 package.json 文件
前端·javascript·npm·node.js·json·nrm·package.json
星马梦缘10 小时前
算法与数据结构
数据结构·c++·算法·动态规划·克鲁斯卡尔·kahn
你的冰西瓜10 小时前
C++中的array容器详解
开发语言·c++·stl
GISer_Jing10 小时前
智能体工具使用、规划模式
人工智能·设计模式·prompt·aigc
Ccjf酷儿10 小时前
C++语言程序设计 (郑莉)第六章 数组、指针和字符串
开发语言·c++
陌路2010 小时前
C++28 STL容器--array
开发语言·c++