ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

nlohmann/json(JSON for Modern C++)序列化全指南:dump()、流输出、pretty-print 与格式化

nlohmann/json(JSON for Modern C++)序列化全指南:dump()、流输出、pretty-print 与格式化 nlohmann/jsonJSON for Modern C序列化全指南dump()、流输出、pretty-print 与格式化【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json序列化Serialization是把内存中的 JSON 值重新还原为 JSON 文本的过程是解析Parsing的逆操作。本文以本项目文档 docs/mkdocs/docs/features/serialization.md 为主线结合核心 API 文档与底层实现 serializer.hpp系统讲解dump()的全部参数、operator流式输出、UTF-8 与非法字节处理、NaN/无穷值/二进制值的行为以及 C20std::format/std::print与 {fmt} 库的格式化用法。读完本文你将能精确控制 JSON 输出的缩进、转义与容错策略并避开序列化过程中的各类坑。序列化把 JSON 值变回 JSON 文本在 nlohmann/json 中序列化指把一个basic_json值转换成一段合法的 JSON 文本与 解析 恰好相反。这一操作的核心入口是成员函数dump()它把值序列化为一个字符串string_t返回#include nlohmann/json.hpp using json nlohmann::json; json j {{pi, 3.141}, {happy, true}}; std::string s j.dump(); // {happy:true,pi:3.141}如果希望把值直接写入某个输出流例如文件或std::cout库提供了 流运算符operatorstd::cout j std::endl;值得一提的是流输出在内部仍然调用dump()但其缩进级别由流的width可配合std::setw控制缩进字符由流的fill可配合std::setfill控制例如std::setw(4)等价于dump(4)。dump 返回的是JSON 文本不是裸值一个常见的误解是dump()会把字符串值原样取出。请务必记住dump()永远返回一段 JSON 文本。因此当 JSON 值本身是字符串时序列化结果会包含包围它的双引号并对特殊字符做转义json j hello; std::string s j.dump(); // hello含引号若想取得字符串内部的实际内容不带引号、不转义应使用类型转换getstd::string()而不是dump()。dump() 的四个参数详解dump()的完整签名见 dump API 文档如下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;indent非负时启用 pretty-print每级缩进该数量的空格0只插入换行、不插入空格-1默认为最紧凑的单行形式。indent_char当indent 0时使用的缩进字符默认是空格可传\t等任意字符。ensure_ascii为true时把全部非 ASCII 字符以\uXXXX序列转义使输出只含 ASCII 字符。error_handler遇到非法 UTF-8 时的处理策略取值见error_handler_t默认strict。该 API 参考了 Python 的json.dumps()目前支持其indent与ensure_ascii两个参数。从实现看dump()具有强异常安全保证抛出异常时不会对任何 JSON 值造成改动且整体复杂度为线性。紧凑输出与 Pretty-print默认情况下dump()产生最紧凑的表示不含任何多余空白。传入非负的indent后输出会按每级若干空格进行美化。文档配套的完整示例见 examples/dump.cpp其输出见 examples/dump.output#include iostream #include nlohmann/json.hpp using json nlohmann::json; int main() { // create JSON values json j_object {{one, 1}, {two, 2}}; json j_array {1, 2, 4, 8, 16}; json j_string Hellö !; // call dump() std::cout objects: \n j_object.dump() \n\n j_object.dump(-1) \n\n j_object.dump(0) \n\n j_object.dump(4) \n\n j_object.dump(1, \t) \n\n; // ... 数组与字符串部分类似 }不同参数对应的实际输出为objects: {one:1,two:2} // dump()最紧凑 {one:1,two:2} // dump(-1)等价于默认 { one: 1, // dump(0)只换行不缩进 two: 2 } { one: 1, // dump(4)每级 4 空格 two: 2 } { one: 1, // dump(1, \t)缩进字符改为制表符 two: 2 } arrays: [1,2,4,8,16] // dump() 紧凑形式 [ 1, // dump(4) 2, 4, 8, 16 ]观察输出可以总结出三条规则缩进字符可通过第二个参数修改例如制表符\tindent为0时只插入换行、不插入前导空格默认indent -1选择紧凑单行形式。这些行为与底层实现一一对应。在 serializer.hpp 中dump()会对对象/数组进行递归序列化紧凑模式下对象成员写作key:value而 pretty-print 模式下写作key: value并在元素间追加,\n与前导缩进。缩进字符串在构造时预分配初始indent_string为 512 个字符若递归层级超出则按两倍扩容从而避免逐层拼接带来的开销。非 ASCII 字符与 ensure_ascii字符串在库内部以 UTF-8 存储与序列化见 类型说明 中关于字符串的部分。默认情况下dump()会把合法的非 ASCII 字符原样拷贝到输出而将第三个参数ensure_ascii设为true则会把所有非 ASCII 字符转义成\uXXXX序列使输出严格只含 ASCIIjson j 苹果; j.dump(); // 苹果 j.dump(-1, , true); // \u82f9\u679c全 ASCII在官方示例中Hellö !的默认输出与ensure_ascii输出分别是Hellö ! Hell\u00f6 \ud83d\ude00!即ö转义为\u00f6而 emoji属于补充平面、由代理对编码被转义为\ud83d\ude00。ensure_ascii输出对下游系统若只支持 ASCII 编码例如某些日志管道、遗留存储非常实用。处理非法 UTF-8strict / replace / ignore如果一个字符串内部含有非法 UTF-8 序列例如其中存放的是 Latin-1 等其他编码的数据默认的strict模式下序列化会直接失败并抛出type_error.316异常。dump()的第四个参数error_handler用于选择处理策略其取值定义在枚举error_handler_t源码见 serializer.hpp取值行为strict默认遇到非法 UTF-8 时抛出type_error.316异常replace用 Unicode 替换字符 UFFFD替代非法字节ignore静默丢弃非法字节合法字节原样拷贝配套示例 examples/error_handler_t.cpp 构造了含非法字节\xA9的字符串ä\xA9ü三种策略的运行结果见 examples/error_handler_t.output为[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9 string with replaced invalid characters: äü string with ignored invalid characters: äü即strict抛出含非法字节索引与字节值的异常信息replace输出替换字符ignore则直接把非法字节删去。序列化不可信输入的注意事项API 文档特别提醒当序列化的数据可能含非法或不可信的 UTF-8例如直接取自网络输入、未经校验的字节流时默认的strict模式会抛出异常。在崩溃敏感路径上处理这类数据应当显式传入error_handler_t::replace替换为 UFFFD或error_handler_t::ignore丢弃非法字节或用try/catch包裹dump()调用。避免非法 UTF-8 的最佳实践最根本的修复方式是在存入JSON 值之前就确保所有字符串均为合法 UTF-8。宽字符wchar_t/std::wstring或 Latin-1 等编码的转换方法可参考 FAQ 中关于非 ASCII 字符的解析错误说明。从源码实现看转义过程由dump_escaped()serializer.hpp 中的私有成员完成它对字符串逐字节进行 UTF-8 状态机校验常量UTF8_ACCEPT/UTF8_REJECT用于维护解码状态同时完成控制字符的\uXXXX转义、ensure_ascii处理与非法字节的判定最终按error_handler决定抛异常、替换还是丢弃。数字、NaN/无穷值与二进制值数字保证 round-trip 的精度浮点数在 JSON 中没有精度的概念但 nlohmann/json 会以足够往返round-trip的精度序列化数字即序列化→再解析后数值保持不变详见 数字处理文档。底层实现serializer.hpp做了分层处理若number_float_t是 IEEE-754 单精度或双精度浮点通过is_iec559、digits、max_exponent检测则调用detail::to_chars()头文件 to_chars.hpp使用 Grisu2 算法生成最短的、保证strtof/strtod往返的十进制表示对于其他浮点类型如long double相关配置回退到snprintf的%.*g/%.*Lg格式并显式移除千分位分隔符、把本地化小数点改写为.必要时补写.0确保输出始终是符合 JSON 语法的数字字面量。NaN 与无穷值序列化为 nullNaN与±infinity无法用 JSON 表示因此dump()会把它们序列化为null。这在源码中有直接体现dump_float()开头即判断if (!std::isfinite(x))并输出nullserializer.hpp。若需要保留这些特殊值应改用 二进制格式CBOR、MessagePack 等进行序列化。二进制值仅作调试用的辅助对象binary_t类型的二进制值没有对应的 JSON 表示dump()会将其序列化为一个包含两个键的辅助对象bytes以整数数组形式给出的字节序列subtype子类型整数若没有子类型则为null。例如紧凑输出形如{bytes:[1,2,3],subtype:null}。从 serializer.hpp 的实现可见二进制值被当作对象输出并通过has_subtype()判断是否渲染subtype。注意这只是调试用途的表示无法再被parse()还原为原始二进制数据parse不会把此类对象自动转回 binary 值想要无损往返请使用 二进制格式 或 binary_values 相关说明。使用 std::format、std::print 与 {fmt}从版本 3.12.0 起只要标准库提供format头由宏JSON_HAS_STD_FORMAT控制JSON 值就可以直接用 C20 的std::format格式化。这是通过std::formatterbasic_json特化实现的该特化同样让 JSON 值可用于std::format_to以及 C23 的std::print/std::printlnstd::print({}, j); // 紧凑形式等价于 j.dump() std::print({:2}, j); // 以缩进 2 美化输出等价于 j.dump(2) std::println({:#}, j); // 以默认缩进美化输出等价于 j.dump(4)格式化规格与dump()参数一一对应{}紧凑序列化行为同dump(){:#}alternate form行为同dump(4)即默认 4 空格缩进美化宽度如{:2}或{:#2}等价于dump(width)——宽度本身即隐含美化输出因为缩进宽度对紧凑输出没有意义fill-and-align 前缀如{:.#}、{:.3}用于指定自定义缩进字符等价于dump(indent, indent_char)。其中对齐方向字符//^对 JSON 值没有独立含义仅其前面的填充字符生效。需要留意的是上述格式规格只支持这一子集其余标准 format 规格正负号、0标志、精度、L、动态宽度{:{}}、尾部类型字符等会抛出std::format_error。特化仅对char版basic_json生效。对于 {fmt} 库fmtlib库内附带了一个format_as辅助函数。注意其行为依赖fmt的版本具体细节与完整的fmt::formatter特化配方参见 FAQ 相关条目。两个格式化入口的对照可参考 std_formatter 示例 及其 输出。序列化到其他格式除 JSON 文本外同一个值还可以序列化为更紧凑的 二进制格式BJData、BSON、CBOR、MessagePack、UBJSON。这些格式的序列化由 binary_writer.hpp 承担它们是dump()之外按需选用的补充通道例如保留 NaN/无穷值与二进制类型。底层dump 是如何分派的把 JSON 值序列化为文本的核心逻辑集中在 serializer.hpp 的serializer::dump()它首先通过switch按value_t类型分派然后对每种类型执行专门的输出例程对象/数组递归调用自身空对象/数组直接写{}/[]字符串与对象键交给dump_escaped()做转义整数由dump_integer()使用 100 项两位数字查找表从后向前生成十进制字符受 Andrei Alexandrescu Fastware 的 fast int2ascii 思路启发避免反转结果浮点如前所述IEEE-754 单/双精度走 Grisu2to_chars其余走snprintf并做本地化清洗布尔/null分别输出true/false/nulldiscarded输出discarded仅在异常内部场景出现。整个序列化过程对流的写入统一通过output_adapter抽象完成这也是为什么同一个 serializer 既能写std::string又能直接写任意std::ostream。单元测试对序列化行为的覆盖可见 tests/src/unit-serialization.cpp 与 tests/src/unit-unicode*.cpp。版本演进小结dump()及相关特性的演进历史见 dump API 文档 的 Version history整理如下1.0.0dump()首次引入3.0.0新增缩进字符indent_char、ensure_ascii选项与异常处理并弃用旧式流运算符j o4.0.0 将移除请改用o j3.4.0新增错误处理枚举error_handler_t3.8.0新增二进制值的序列化支持3.12.0支持经std::format系列接口格式化 JSON 值受JSON_HAS_STD_FORMAT控制。总结与延伸阅读序列化是 nlohmann/json 使用频率最高的操作之一。掌握dump()的indent/indent_char/ensure_ascii/error_handler四个参数、区分dump()与getstd::string()、正确处理非法 UTF-8 与不可信输入、理解 NaN/二进制值的特殊行为就能在生产环境中稳定、可控地输出 JSON。如需继续深入建议阅读以下资料dump()序列化为 JSON 字符串operator序列化到输出流to_string()JSON 指针等类型的字符串化辅助std::formatterbasic_json配合std::format/std::print使用format_as配合 {fmt} 库使用解析Parsing序列化的逆操作【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表