Oj 入门:Ruby 高性能 JSON 解析与生成
了解 Oj 的 JSON 解析模式、严格输入处理、键类型和文件操作,并检查替换标准库时的序列化兼容性。
Oj 是 Ruby 的 JSON 处理库。选择前先定位解析或序列化是否为实际瓶颈,再对同一数据集比较耗时与内存;类型映射与安全边界比单次速度数字更重要。
基本用法:明确指定模式
gem install oj
require 'oj'
data = Oj.load('{"name":"demo","users":42}', mode: :strict)
json = Oj.dump({ 'name' => 'demo', 'tags' => ['a', 'b'] }, mode: :strict)
外部输入优先使用普通 JSON 模式,并限制输入大小。不要将支持 Ruby 对象恢复的模式直接用于不可信数据。全局替换 JSON 行为前,应覆盖特殊字符、数字、时间及自定义对象的兼容性测试。
解析模式:按需选择行为
Oj 的 :mode 选项决定输出结构:
:object:支持 Ruby 对象序列化与恢复,不能等同普通 JSON,也不应处理不可信输入;:compat:以兼容 JSON gem 为目标,仍需核对选项与自定义序列化行为;:strict:严格 JSON 规范,不允许 NaN/Infinity 等非标值;:custom:自定义映射规则(如把特定键直接实例化为 Ruby 对象);:wab:面向 WAB 对象模型的模式。
应明确指定所需模式,避免依赖全局默认值。
键类型与符号化
Oj.load('{"a":1}', symbol_keys: true) # => {:a=>1}
symbol_keys: true 让所有键变成 Symbol,省去后续 deep_symbolize_keys。注意:处理外部不可信 JSON 时不要盲目符号化——恶意构造的海量不同键会撑爆 symbol 表(老版本 Ruby 的 symbol 不回收,新版本虽回收但仍是无谓开销)。
格式化输出与文件处理
Oj.to_file('data.json', { 'a' => 1 }, mode: :strict) # 写文件
Oj.load_file('data.json', mode: :strict) # 读文件
Oj.dump({ 'a' => 1 }, mode: :strict, indent: 2) # 美化输出
大文件处理配合 Oj::ScHandler 的 SAX 式回调解析,可以在不全量加载的情况下流式提取字段——处理 GB 级 JSON 导出文件的利器。
调试解析问题
Oj 报错信息比标准库更精确(带行列位置),排查坏 JSON 时先开 :strict 模式定位语法错误;遇到类型映射问题(如 BigDecimal、Time),检查 :create_additions 选项与自定义类的序列化约定。与标准库混用时统一入口,避免同一对象在两个库里序列化结果不同。
常见问题(FAQ)
Q:Oj 和标准库 JSON 结果完全一致吗?
绝大多数场景一致,细节有差异:浮点精度处理、非 UTF-8 字符、Time/BigDecimal 等特殊类型的默认序列化。替换前跑一遍测试套,重点覆盖含特殊字符和自定义类型的用例。
Q:Rails 项目怎么全局启用 Oj?
先按所选 Oj 与 Rails 版本确认适配入口,再评估 Oj.optimize_rails 或兼容层;不要同时叠加多个全局替换。替换后跑全量测试,确认序列化输出无回归。
Q:Oj 能处理 JSON 流(JSON Lines)吗?
可以。JSONL 格式按行读取逐行 Oj.load 即可;需要更高性能时用 Oj 的 sc 模式(Simple Callback)边读边处理,内存使用取决于输入缓冲和回调中保留的数据。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。