← 中文资源

简体中文版本

Serde JSON 流式数组反序列化

用 serde_json 的 StreamDeserializer 增量解析大 JSON 数组:逐元素产出、坏元素隔离计数、同样支持从 Reader 流式消费。

阅读英文原文

示例说明

from_str 解析整棵 JSON 树要先把全部文本读入并构建完整值——数百万元素的数组内存翻数倍。StreamDeserializer 把反序列化拆成逐元素迭代:每个元素解析完即可使用,坏元素只作废自身不拖垮整批。剥掉外层方括号后,元素流的两种来源(字符串切片与 Reader)共用同一套迭代器。

技术要点

  • 逐元素迭代替代整树解析
  • 坏元素跳过计数不中断
  • from_reader 免全文驻留内存

代码解读

parse_array_items 先剥去数组的外括号得到元素序列,into_iter:: 构建强类型迭代器;while 循环里 Ok 收集 Err 计数。main 的样例数组里第三个元素的温度是字符串类型,反序列化失败被隔离,其余三条正常解析。Cursor 分支演示 Reader 来源的同一流程。

使用建议

真实流式场景配 NDJSON 更自然,此模式适合既定 JSON 数组结构。元素间分隔符要规范——多余逗号或空白会影响迭代器推进。大文件 Reader 来源配 BufReader 提升吞吐。

代码示例

use serde::Deserialize;
use serde_json::Deserializer;
use std::io::Cursor;

#[derive(Debug, Deserialize)]
struct Reading {
    sensor: String,
    celsius: f32,
}

fn parse_array_items(raw: &str) -> (Vec<Reading>, usize) {
    // 逐元素反序列化:坏元素跳过并计数
    let mut items = Vec::new();
    let mut rejected = 0;

    let mut de = Deserializer::from_str(trim_brackets(raw)).into_iter::<Reading>();
    while let Some(result) = de.next() {
        match result {
            Ok(reading) => items.push(reading),
            Err(_) => rejected += 1,
        }
    }
    (items, rejected)
}

fn trim_brackets(raw: &str) -> &str {
    let trimmed = raw.trim();
    let body = trimmed
        .strip_prefix('[')
        .and_then(|s| s.strip_suffix(']'))
        .unwrap_or(trimmed);
    body
}

fn main() {
    let payload = r#"[
        {"sensor":"hall-1","celsius":21.4},
        {"sensor":"hall-2","celsius":20.9},
        {"sensor":"hall-3","celsius":"not-a-number"},
        {"sensor":"hall-4","celsius":22.1}
    ]"#;

    let (items, rejected) = parse_array_items(payload);
    for reading in &items {
        println!("{} -> {:.1}C", reading.sensor, reading.celsius);
    }
    println!("parsed: {}, rejected: {}", items.len(), rejected);

    // Cursor 演示从 Reader 流式消费同一结构
    let cursor = Cursor::new(trim_brackets(payload).as_bytes().to_vec());
    let stream = Deserializer::from_reader(cursor).into_iter::<Reading>();
    println!("reader-based count: {}", stream.filter_map(|r| r.ok()).count());
}