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

5
我希望能将一串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)?;

5
抱歉,我只能使用英文进行回答。對於需要翻譯的內容,以下是翻譯結果:你不能這樣做,因為 JSON 是一種無序的數據格式。為什麼要這樣做呢?唯一的方法是在有序的哈希映射表中解析你想要有序的每個字段,可以使用 https://lib.rs/crates/indexmap ,但你會失去很多使 serde 好用的功能。 - Stargateur
1
你所期望的行为意味着它不再是JSON,只是类似JSON的一种形式。 然而,对于序列化来说,有一个preserve_order的箱特性可以在创建字符串时保持结构体的顺序作为允许的许多顺序之一。 - Zólyomi István
2
感谢您的评论。您说得对,这不是严格的JSON格式要求。需要这样做的原因是该对象经过加密签名,因此顺序变得很重要。我希望其他人可能已经解决了这个问题;)我将查看indexmappreserve_order。再次感谢。 - mycognosist
如果您想确保负载与签名匹配,则可以这样做。我不确定您希望通过尝试强制执行严格的反序列化格式来获得什么好处。无论如何,您仍需要单独验证它,因为“age:43”和“age:45”将被正确格式化,但是会出现错误。 - kmdreko
1个回答

0

您可以通过提供自定义的Deserialize实现来完成这个任务。

对于JSON,您将会使用Visitor::visit_map()函数进行struct反序列化。通常情况下,结构字段被按照给定的顺序访问(例如,当您使用#[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 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),
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接