myc*_*ist 5 serialization json rust json-deserialization serde
我想将一串 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)?;
Run Code Online (Sandbox Code Playgroud)
您可以通过提供自定义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)
}
}
Run Code Online (Sandbox Code Playgroud)
这里有很多样板(通常隐藏在后面#[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)
}
Run Code Online (Sandbox Code Playgroud)
打印此输出:
[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),
)
Run Code Online (Sandbox Code Playgroud)