Может ли Serde десериализовать JSON для одного из набора типов в зависимости от значения поля? - PullRequest
0 голосов
/ 27 октября 2018

У меня есть группа различных сообщений, которые поступают как 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")

    }

}

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

1 Ответ

0 голосов
/ 27 октября 2018

Нет смысла оставлять «один» или «два» в вашей структуре 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)
...