DEBUG.WIKI
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →

serde_json 读空结构体报 trailing characters,JSON 却有效

同一份 JSON 能读成 Value,读取空结构体时却报 trailing characters。检查对象位置是否实际收到数组,以及派生结构体的序列读取入口。

JSON 能正常读成 serde_json::Value,读到 HashMap<String, Item> 时却报 trailing characters。如果 Item 是空结构体 struct Item {},先看报错位置的值是否实际上是数组。

例如 {"items":{"example":[1]}} 是有效 JSON,但这里的 [1] 未必符合 Item 的业务含义。把它改成 [] 以后派生反序列化甚至可能成功,这更容易让人误以为是某个数字破坏了 JSON。

结构体也可能从序列入口读取

正式 serde_json 1.0.154 的 deserialize_struct 同时提供对象和数组入口。空结构体的序列 visitor 不需要读取字段;面对非空数组,visitor 返回后还有元素没有消费,解析器检查数组结尾时便报告 trailing characters。

因此,这条信息不只可能出现在“完整 JSON 后面又拼了字符”的场景。它也可能源于目标结构体与当前值的形状不匹配。不能根据错误位置就删掉数组内容,或关闭输入结束检查。

输入本来就是数组,就声明数组对应的类型

若 example 按协议应包含一组数值,目标应采用相应的 Vec 或其他集合结构,保留元素类型和范围校验。空结构体不能代替“还没来得及设计的任意对象”,也不能靠先把数组清空来让导入完成。

若协议明确只允许空对象,可在这个字段的读取边界先检查对象形状。下面只展示单个 Item 的读取入口:

use serde_json::Value;

fn read_empty_object(input: &str) -> Result<(), Box<dyn std::error::Error>> {
    let value: Value = serde_json::from_str(input)?;
    let object = value.as_object().ok_or("expected an object")?;
    if !object.is_empty() {
        return Err("expected an empty object".into());
    }
    Ok(())
}

这个入口要求严格空对象,因此 {} 通过,[]、[1]、null 和非空对象都应被拒绝。若实际 Item 允许字段,应继续按字段定义解析,不能保留 is_empty() 限制,也不能仅检查 is_object() 就跳过所有字段校验。

在完整对象里,可把同样的检查放进 Item 的自定义 Deserialize 或独立 wire 转换,返回应用的正常错误类型。不要先把整份业务结构读成 Value 后只验证 example,就忽略其他必填字段、重复键策略或范围要求。

这条处理针对目标类型的入口约定,不需要重写 JSON 文本。复查时保留空对象、非空对象、空数组、非空数组和真正的尾随内容,分别确认成功与失败符合协议;“Value 能读”只能证明基本 JSON 语法,不证明业务模型匹配。