【问题标题】:How to ignore unknown enum variant while deserializing?反序列化时如何忽略未知的枚举变体?
【发布时间】:2021-05-26 09:56:43
【问题描述】:

我有几个非详尽的枚举,我需要很好地处理它们。 当检测到未知变体时,我需要简单地忽略值并继续处理其他变体。

我目前正在反序列化来自的数据向量,并设法为我的应用程序正确获取 MyStruct 的向量。

我的应用程序需要与新版本的枚举向前兼容,并且简单地忽略未知的变体。

例如,目前:

use serde::{Deserialize};

#[derive(Deserialize, Debug)]
#[non_exhaustive]
pub enum CaseStyle {
    Lowercase,
    Uppercase,
}

#[derive(Deserialize, Debug)]
#[non_exhaustive]
pub enum Encoding {
    Plain,
    Base64,
}

#[derive(Deserialize, Debug)]
pub struct MyStruct {
    case_style: CaseStyle,
    encoding: Encoding,
}

fn main() {
    let j = r#"[
    {"case_style": "Lowercase","encoding":"Plain"},
    {"case_style": "Snakecase","encoding":"Plain"},
    {"case_style": "Lowercase","encoding":"Aes"},
    {"case_style": "Uppercase","encoding":"Base64"}
    ]"#;

    // Convert the JSON string to vec.
    let deserialized: Vec<MyStruct> = serde_json::from_str(&j).unwrap();

    // Prints deserialized = [MyStruct { case_style: Lowercase, encoding: Plain }, MyStruct { case_style: Uppercase, encoding: Base64 }]
    println!("deserialized = {:?}", deserialized);
}

此示例失败,因为 json 数据中有 2 个未知变体。我怎么能从反序列化中忽略这些未知变体?

【问题讨论】:

标签: rust deserialization serde


【解决方案1】:

您可以通过将所有反序列化错误转换为 None 并将所有成功转换为 Some(...) 来反序列化 Vec&lt;Option&lt;MyStruct&gt;&gt;。之后,您可以通过 flattening 删除 Option。您可以跳过Option,但这需要您为Vec 编写自定义反序列化器。

基于serde_with crate:

use serde::Deserialize;
use serde_with::{serde_as, DefaultOnError};

#[derive(Deserialize, Debug)]
#[non_exhaustive]
pub enum CaseStyle {
    Lowercase,
    Uppercase,
}

#[derive(Deserialize, Debug)]
#[non_exhaustive]
pub enum Encoding {
    Plain,
    Base64,
}

#[derive(Deserialize, Debug)]
pub struct MyStruct {
    case_style: CaseStyle,
    encoding: Encoding,
}

fn main() {
    let j = r#"[
        {"case_style": "Lowercase","encoding":"Plain"},
        {"case_style": "Snakecase","encoding":"Plain"},
        {"case_style": "Lowercase","encoding":"Aes"},
        {"case_style": "Uppercase","encoding":"Base64"}
        ]"#;

    #[serde_as]
    #[derive(Deserialize)]
    struct W(#[serde_as(as = "Vec<DefaultOnError>")] Vec<Option<MyStruct>>);

    // Convert the JSON string to vec.
    let deserialized: Vec<MyStruct> = serde_json::from_str::<W>(&j)
        .unwrap()
        .0
        .into_iter()
        .flatten()
        .collect();

    // Prints deserialized = [MyStruct { case_style: Lowercase, encoding: Plain }, MyStruct { case_style: Uppercase, encoding: Base64 }]
    println!("deserialized = {:?}", deserialized);
}

【讨论】:

  • 我在想这样的事情,但实际的序列化结构隐藏在一个更复杂的结构中。无论如何,似乎这样的事情可以在 DeserializeSeed 中实现,如 here
【解决方案2】:
use serde::{Deserialize, Deserializer};
use serde_with::{serde_as, DefaultOnError};

#[derive(Deserialize, Debug)]
#[non_exhaustive]
pub enum CaseStyle {
    Lowercase,
    Uppercase,
}

#[derive(Deserialize, Debug)]
#[non_exhaustive]
pub enum Encoding {
    Plain,
    Base64,
}

#[derive(Deserialize, Debug)]
pub struct MyStruct {
    case_style: CaseStyle,
    encoding: Encoding,
}


#[derive(Deserialize, Debug)]
pub struct VecMyStruct {
    #[serde(deserialize_with = "skip_on_error")]
    items: Vec<MyStruct>,
}
fn skip_on_error<'de, D>(deserializer: D) -> Result<Vec<MyStruct>, D::Error>
where
    D: Deserializer<'de>,
{
    #[serde_as]
    #[derive(Deserialize, Debug)]
    struct MayBeT(#[serde_as(as = "DefaultOnError")] Option<MyStruct>);

    let values: Vec<MayBeT> = Deserialize::deserialize(deserializer)?;

    Ok(values.into_iter().filter_map(|t| t.0).collect())
}

fn main() {
    let j = r#"{"items":[
    {"case_style": "Lowercase","encoding":"Plain"},
    {"case_style": "Snakecase","encoding":"Plain"},
    {"case_style": "Lowercase","encoding":"Aes"},
    {"case_style": "Uppercase","encoding":"Base64"}
    ]}"#;

    // Convert the JSON string to vec.
    let deserialized: VecMyStruct = serde_json::from_str(&j).unwrap();

    // Prints deserialized = VecMyStruct { items: [MyStruct { case_style: Lowercase, encoding: Plain }, MyStruct { case_style: Uppercase, encoding: Base64 }] }
    println!("deserialized = {:?}", deserialized);
}

感谢@jonasbb,我找到了这种方法。我现在唯一的问题是有一个更通用的“skip_on_error”方法,它可以采用任何T: Deserialize&lt;'de&gt; 而不仅仅是MyStruct。一旦我添加了T 类型,编译器就会在为MaybeT 实现Deserialization 时告诉我错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-29
    • 2019-09-16
    • 1970-01-01
    • 1970-01-01
    • 2012-10-19
    相关资源
    最近更新 更多