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 语法,不证明业务模型匹配。


