nlohmann/json 的 dump() 序列化不可信非 UTF-8 数据抛出 type_error.316 怎么处理
【免费下载链接】jsonJSON for Modern C++项目地址: https://gitcode.com/GitHub_Trending/js/json
用 nlohmann/json 序列化从网络、文件或外部输入得到的字符串时,如果字符串不是合法的 UTF-8 编码,dump()会在默认的strict模式下抛出[json.exception.type_error.316]。这篇文章讲清楚这个异常的来源,并给出文档给出的三条处理路径:给dump()传非 strict 错误处理器、用 try/catch 捕获、或在存入 JSON 之前先把数据规范化为 UTF-8。
先确认异常来源
type_error.316的定义见 exceptions.md 的 json.exception.type_error.316 一节:dump()只接受 UTF-8 编码的字符串——如果把一个非 UTF-8 的std::string赋给 JSON 值,dump()就会抛这个异常。文档给出的示例信息(示例结果,实际 index 和字节值随你的数据变化):
[json.exception.type_error.316] invalid UTF-8 byte at index 15: 0x6F信息中的index是出错字节在字符串中的位置,后面的十六进制是该字节的值,可以直接用来定位是哪个字节不合法。
FAQ 对这类崩溃的定性见 faq.md 的 Serializing untrusted or invalid UTF-8:dump()在默认strict模式下抛出未捕获的type_error.316(例如 CVE-2024-34363 中报告的崩溃)属于使用方式问题,不是库的漏洞,因为 RFC 8259 要求 JSON 文本必须是合法 UTF-8。
另外注意一个版本前提:dump()的error_handler参数是3.4.0 版本加入的(见 dump 文档的 Version history)。如果你的版本低于 3.4.0,没有下面第一、二节里的非 strict 选项,只能靠 try/catch 或数据规范化处理。
用文档示例复现三种行为
仓库里有一个现成的最小复现程序 error_handler_t.cpp:构造一个含非法 UTF-8 字节序列的字符串"ä\xA9ü",分别用默认dump()、replace和ignore三种方式序列化:
#include <iostream> #include <nlohmann/json.hpp> using json = nlohmann::json; int main() { // create JSON value with invalid UTF-8 byte sequence json j_invalid = "ä\xA9ü"; try { std::cout << j_invalid.dump() << std::endl; } catch (const json::type_error& e) { std::cout << e.what() << std::endl; } std::cout << "string with replaced invalid characters: " << j_invalid.dump(-1, ' ', false, json::error_handler_t::replace) << "\nstring with ignored invalid characters: " << j_invalid.dump(-1, ' ', false, json::error_handler_t::ignore) << '\n'; }按 error_handler_t.output 给出的文档示例输出(示例结果):
[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9 string with replaced invalid characters: "ä<replacement character>ü" // 中间是 U+FFFD string with ignored invalid characters: "äü"这组输出把三种行为一次演示完:默认模式抛异常且报错信息精确到字节位置;replace把非法字节替换为 U+FFFD 后输出;ignore把非法字节直接丢弃。
处理方式一:给 dump() 传非 strict 错误处理器
dump()的完整签名(见 dump 文档):
string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, const error_handler_t error_handler = error_handler_t::strict) const;第四个参数error_handler的三种取值定义在 error_handler_t 文档:
| 取值 | 行为 |
|---|---|
strict(默认) | 遇到非法 UTF-8 抛出type_error(即 type_error.316) |
replace | 把非法 UTF-8 序列替换为 U+FFFD(REPLACEMENT CHARACTER) |
ignore | 忽略非法 UTF-8 序列:合法字节原样拷贝到输出,非法字节被丢弃 |
FAQ 给出的推荐写法就是把非 strict 处理器作为最后一个参数传入:
// replace invalid sequences with U+FFFD instead of throwing const auto s = j.dump(-1, ' ', false, json::error_handler_t::replace);replace和ignore的差异在于:前者输出中会留下 U+FFFD 占位,长度信息大致保留;后者直接少字节。选择哪一种取决于下游消费者能否接受替换字符。
处理方式二:保留 strict 模式,用 try/catch 捕获
dump 文档的 "Serializing untrusted input" 警告明确指出:在崩溃敏感路径上序列化不可信输入时,要么选择非 strict 错误处理器,要么把dump()包在try/catch里。上面的复现程序演示了后者:捕获json::type_error,通过e.what()打印异常信息,程序继续运行而不是崩溃:
try { std::cout << j_invalid.dump() << std::endl; } catch (const json::type_error& e) { std::cout << e.what() << std::endl; }这条路径适合 strict 的"发现即报错"语义对你有用的场景:异常信息中的字节位置和十六进制值本身就是排查线索。注意dump()提供强异常保证——抛出异常时不会修改任何 JSON 值。
处理方式三:存入 JSON 之前先规范化为 UTF-8
前两节是在序列化端容忍非法字节,更彻底的做法是保证存入库里的字符串本身就是 UTF-8。serialization.md 的 tip建议:最好的修复是在存储之前确保所有字符串都是 UTF-8 编码;exceptions.md 的 316 一节也给了同样的建议(把源文件以 UTF-8 编码保存,并对确实无法避免的字节传错误处理器)。
FAQ 明确了编码支持范围(见 faq.md 的 Parse errors reading non-ASCII characters):
- 只支持 UTF-8 编码输入,这是 RFC 8259 规定的 JSON 默认编码;
std::u16string、std::u32string按 UTF-16/UTF-32 解析,但只限于直接传入,从文件或其他输入容器读取时不支持这两种编码;- Latin-1、ISO 8859-1 等其他编码不受支持,会产生解析或序列化错误(Windows 上 Latin-1/ISO 8859-1 很常见,是这一类报错的典型来源)。
如果数据是宽字符串,FAQ 的 Wide string handling 一节给出了文档示例的转换函数(std::wstring直接存入会被 dump 成数字数组,需要先换编码):
#include <codecvt> // codecvt_utf8 #include <locale> // wstring_convert // encoding function std::string to_utf8(std::wstring& wide_string) { static std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv; return utf8_conv.to_bytes(wide_string); } json j; std::wstring ws = L"車B1234 こんにちは"; j["encoded"] = to_utf8(ws);对于 Latin-1 等编码,文档只说明其不受支持、需要转换,未提供具体转换代码;序列化文档建议的做法是"确保存入前为 UTF-8",具体转换按你的数据来源自行实现,转换后再走默认的strict序列化。
如何判断下一步
- 报错信息形如
[json.exception.type_error.316] invalid UTF-8 byte at index N: 0xNN:先确认这个 JSON 值里的字符串来自哪里,它不是 UTF-8。 - 数据来自网络等不可信来源,且这条路径不能因序列化而崩溃:按 dump 文档警告的要求,改为
replace/ignore,或加 try/catch。 - 数据本应是 UTF-8 但来源编码不对(典型是 Windows 上的 Latin-1):优先在入库前转换编码,让
strict模式继续作为质量检查。 - 限制提醒:
replace/ignore只能保证"不抛异常并产出某种输出",被替换或被丢弃的字节内容不可恢复;库本身也不会替你替换 Unicode noncharacters 或修复不完整代理对(见 FAQ 编码支持一节)。
相关文档入口:dump()、error_handler_t、type_error 异常表、FAQ、Serialization 特性文章。
【免费下载链接】jsonJSON for Modern C++项目地址: https://gitcode.com/GitHub_Trending/js/json
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考