【问题标题】:Can I map types when deserializing with "Serde"?使用“Serde”反序列化时可以映射类型吗?
【发布时间】:2021-03-13 08:49:54
【问题描述】:

基本上我有以下数据结构:

enum Value {
    Scalar(f64),
    Vector3((f64, f64, f64)),
}

struct Data {
    attribute: Value,
}

使用serde/serde_json 进行序列化会得到以下结果

{
    "attribute": { "Scalar": 1.0 }  
}
{
    "attribute": { "Vector3": [ 1.0, 2.0, 3.0 ] }
}

反序列化按预期工作。但是,是否可以将以下反序列化为相同的数据结构?

{
    "attribute": 1.0  
}
{
    "attribute": [ 1.0, 2.0, 3.0 ]
}

是否可以“映射”f64 到 Scalar(f64) 和 Vec<f64> 到 Vector3((f64, f64, f64))?


这两种形式都应该有效。如果以下最小示例有效,那就太好了:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
enum Value {
    Scalar(f64),
    Vector3((f64, f64, f64)),
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
struct Data {
    attribute: Value,
}

fn main() {
    let js1 = r#"
    {
        "attribute": { "Scalar": 1.0 }  
    }"#;
    let d1: Data = serde_json::from_str(js1).unwrap();
    println!("{:?}", d1);

    let js2 = r#"
    {
        "attribute": { "Vector3": [ 1.0, 2.0, 3.0 ] }
    }"#;
    let d2: Data = serde_json::from_str(js2).unwrap();
    println!("{:?}", d2);

    let js3 = r#"
    {
        "attribute": 1.0  
    }"#;
    let d3: serde_json::Result<Data> = serde_json::from_str(js3);
    match d3 {
        Ok(d3) => println!("{:?}", d3),
        Err(e) => println!("{:?}", e),
    }

    let js4 = r#"
    {
        "attribute": [ 1.0, 2.0, 3.0 ] 
    }"#;
    let d4: serde_json::Result<Data> = serde_json::from_str(js4);
    match d4 {
        Ok(d4) => println!("{:?}", d4),
        Err(e) => println!("{:?}", e),
    }
}

输出:

Data { attribute: Scalar(1.0) }
Data { attribute: Vector3((1.0, 2.0, 3.0)) }
Error("expected value", line: 3, column: 22)
Error("expected value", line: 3, column: 22)

【问题讨论】:

  • 您希望两种表单同时工作吗?还是只是第二种形式?
  • @kmdreko 两种形式。如果最小的例子有效,那就太好了。在我的特殊情况下,Value 包含超过 2 个变体。

标签: rust deserialization serde


【解决方案1】:

你可以在你的枚举上使用#[serde(untagged)] 来允许它被“映射”。 Serde doesn't currently support 两者同时存在,但有一个 hack here 虽然我还没有尝试过。您还可以在变体中使用带有标记名称的枚举

例子:

#[serde(untagged)]
enum Value {
    Scalar(f64),
    ScalarTagged {Scalar: f64},
    Vector3((f64, f64, f64)),
    Vector3Tagged {
        Vector3: (f64, f64, f64)
    }
}

这通过了您的最小示例

【讨论】:

  • 你的想法启发了我的回答。
【解决方案2】:

@Raspberry1111 提出了一个广受欢迎的解决方法。但是,仍然欢迎更直接的解决方案。


我不得不使用#[serde (untagged)](参见Enum representations)并更改数据结构以满足我的所有需求:

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
enum TaggedValue {
    Scalar(f64),
    Vector3((f64, f64, f64)),
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
enum Value {
    Scalar(f64),
    Vector3((f64, f64, f64)),
    Tagged(TaggedValue),
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
struct Data {
    attribute: Value,
}

// [...]

输出:

Data { attribute: Tagged(Scalar(1.0)) }
Data { attribute: Tagged(Vector3((1.0, 2.0, 3.0))) }
Data { attribute: Scalar(1.0) }
Data { attribute: Vector3((1.0, 2.0, 3.0)) }

仍然欢迎任何更直接的解决方案

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多