使用serde反序列化JSON时强制执行严格的排序



我想将JSON数据串反序列化为具有多个字段的结构,如果序列化数据的顺序与结构中字段的顺序不匹配,则返回错误。

我已经阅读了serde文档,包括关于自定义序列化的部分,但找不到解决方案。我想通过使用字段名检查实现Deserializer来强制执行严格的排序是可能的,但我对此并不完全确定。

一个遵循serde_json文档格式的示例:

#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
let correct_order = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// this deserializes correctly (no error)
let p: Person = serde_json::from_str(data)?;
let incorrect_order = r#"
{
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
"name": "John Doe"
}"#;
// how to ensure this returns an error? (data fields out of order)
let p2: Person = serde_json::from_str(data)?;

您可以通过提供自定义的Deserialize实现来实现这一点。

对于JSON,您将为struct反序列化执行的访问者函数是Visitor::visit_map()。通常,结构字段会按照给定的顺序进行访问(例如,当您使用#[derive(Deserialize)]时(。我们只需写信给访客,就可以确保字段按照我们期望的严格顺序排列。

use serde::{
de,
de::{Deserialize, Deserializer, MapAccess, Visitor},
};
use std::fmt;
#[derive(Debug)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
impl<'de> Deserialize<'de> for Person {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
// Some boilerplate logic for deserializing the fields.
enum Field {
Name,
Age,
Phones,
}
impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("name, age, or phones")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match v {
"name" => Ok(Field::Name),
"age" => Ok(Field::Age),
"phones" => Ok(Field::Phones),
_ => Err(E::unknown_field(v, FIELDS)),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
// Logic for actually deserializing the struct itself.
struct PersonVisitor;
impl<'de> Visitor<'de> for PersonVisitor {
type Value = Person;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Person with fields in order of name, age, and phones")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
// Deserialize name.
let name = match map.next_key()? {
Some(Field::Name) => Ok(map.next_value()?),
Some(_) => Err(de::Error::missing_field("name")),
None => Err(de::Error::invalid_length(0, &self)),
}?;
// Deserialize age.
let age = match map.next_key()? {
Some(Field::Age) => Ok(map.next_value()?),
Some(_) => Err(de::Error::missing_field("age")),
None => Err(de::Error::invalid_length(1, &self)),
}?;
// Deserialize phones.
let phones = match map.next_key()? {
Some(Field::Phones) => Ok(map.next_value()?),
Some(_) => Err(de::Error::missing_field("phones")),
None => Err(de::Error::invalid_length(2, &self)),
}?;
Ok(Person { name, age, phones })
}
}
const FIELDS: &[&str] = &["name", "age", "phones"];
deserializer.deserialize_struct("Person", FIELDS, PersonVisitor)
}
}

这里有很多样板(通常隐藏在#[derive(Deserialize)]后面(:

  • 首先,我们定义了一个内部枚举Field,用它自己的Deserialize实现来反序列化结构字段。这是一个标准的实现,我们只是在这里手工写出来
  • 然后我们定义一个PersonVisitor来实际提供我们的Visitor特性实现。这部分是我们实际执行字段排序的地方

您可以看到,现在可以按预期工作了。以下代码:

fn main() {
let correct_order = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// this deserializes correctly (no error)
let p: serde_json::Result<Person> = serde_json::from_str(correct_order);
dbg!(p);
let incorrect_order = r#"
{
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
"name": "John Doe"
}"#;
// how to ensure this returns an error? (data fields out of order)
let p2: serde_json::Result<Person> = serde_json::from_str(incorrect_order);
dbg!(p2);
assert!(false)
}

打印此输出:

[src/main.rs:114] p = Ok(
Person {
name: "John Doe",
age: 43,
phones: [
"+44 1234567",
"+44 2345678",
],
},
)
[src/main.rs:128] p2 = Err(
Error("missing field `name`", line: 3, column: 17),
)

最新更新