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

8wigbo56  于 2023-04-08  发布在  其他
关注(0)|答案(1)|浏览(136)

我想将JSON数据字符串反序列化为具有多个字段的结构体,如果序列化数据的顺序与结构体中字段的顺序不匹配,则返回错误。
我已经通读了serde documentation,包括自定义序列化部分,但找不到解决方案。我想可能通过实现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)?;
qij5mzcb

qij5mzcb1#

您可以通过提供一个自定义的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来反序列化struct字段,它有自己的Deserialize实现。这是一个标准实现,我们在这里手工编写它。
  • 然后我们定义一个PersonVisitor来提供我们的Visitor trait实现。这部分是我们实际执行字段排序的地方。

下面的代码是这样的:

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),
)

相关问题