Может ли Serde десериализовать JSON для одного из набора типов в зависимости от значения поля?

У меня есть группа различных сообщений, которые входят как JSON и могут быть различены на основе одного поля, но тогда каждый вариант имеет различную коллекцию вторичных полей:

#[derive(Debug, Serialize, Deserialize)]
struct MessageOne {
    ///op will always be "one"
    op: String,
    x: f64,
    y: f64,
}

#[derive(Debug, Serialize, Deserialize)]
struct MessageTwo {
    ///op will always be "two"
    op: String,
    a: f64,
    b: i64,
}

Различные типы сообщений направляются на разные функции обработки (например, process_message_one, process_message_two, так далее). Существует ли элегантный или идиоматический способ автоматического выбора правильного подтипа сообщения? В настоящее время я определил общее сообщение:

#[derive(Debug, Serialize, Deserialize)]
struct MessageGeneric {
    op: String,
}

затем проанализируйте входящий JSON в MessageGenericпрочитайте op поле, а затем снова десериализовать, сопоставляя op выбрать правильный тип сообщения. Полный пример:

#![allow(unused)]

extern crate serde; // 1.0.78
extern crate serde_json; // 1.0.27

#[macro_use]
extern crate serde_derive;

use std::collections::HashMap;

#[derive(Debug, Serialize, Deserialize)]
struct MessageGeneric {
    op: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct MessageOne {
    ///op will always be "one"
    op: String,
    x: f64,
    y: f64,
}

#[derive(Debug, Serialize, Deserialize)]
struct MessageTwo {
    ///op will always be "two"
    op: String,
    a: f64,
    b: f64,
}

fn process_message_one(m: &MessageOne) {
    println!("Processing a MessageOne: {:?}", m);
}

fn process_message_two(m: &MessageTwo) {
    println!("Processing a MessageTwo: {:?}", m);
}



fn main() {
    let data = r#"{
        "op": "one",
        "x": 1.0,
        "y": 2.0
    }"#;

    let z: MessageGeneric = serde_json::from_str(data).unwrap();

    match z.op.as_ref() {
        "one" => {
            let zp: MessageOne = serde_json::from_str(data).unwrap();
            process_message_one(&zp);
        },
        "two" => {
            let zp: MessageTwo = serde_json::from_str(data).unwrap();
            process_message_two(&zp);
        },
        _ => println!("Unknown Message Type")

    }

}

Я видел представления enum в Serde, но мне было неясно, будет ли / как это применяться в этом случае. Входящие сообщения определяются внешним API, поэтому я не могу контролировать их содержимое, не зная, какие есть варианты.

1 ответ

Нет смысла оставлять "один" или "два" в вашей структуре MessageOne а также MessageTwo: если вы построили эту структуру, вы уже знаете, является ли это первым или вторым сообщением.

extern crate serde; // 1.0.78
extern crate serde_json; // 1.0.27

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "op")]
enum Message {
    #[serde(rename = "one")]
    One { x: f64, y: f64 },
    #[serde(rename = "two")]
    Two { a: f64, b: f64 },
}

fn process_message(message: &Message) {
    println!("Processing a : {:?}", message);
}

use serde_json::Error;

fn main() -> Result<(), Error> {
    let data = r#"{
        "op": "one",
        "x": 1.0,
        "y": 2.0
    }"#;

    let message: Message = serde_json::from_str(data)?;
    process_message(&message);

    let data = r#"{
        "op": "two",
        "a": 1.0,
        "b": 2.0
    }"#;

    let message: Message = serde_json::from_str(data)?;
    process_message(&message);

    let data = r#"{
        "op": "42",
        "i": 1.0,
        "j": 2.0
    }"#;

    let message: Message = serde_json::from_str(data)?;
    process_message(&message);
    Ok(())
}
Standard Output
Processing a : One { x: 1.0, y: 2.0 }
Processing a : Two { a: 1.0, b: 2.0 }

Standard Error
Error: Error("unknown variant `42`, expected `one` or `two`", line: 2, column: 18)
Другие вопросы по тегам