У меня есть группа разных сообщений, которые поступают как 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")
}
}
Я видел Представления Serde enum, но мне было неясно, будет ли / как это применяться в этом случае. Входящие сообщения определяются внешним API, поэтому я не могу контролировать их содержимое, не зная, какие есть варианты.
Нет смысла сохранять «один» или «два» в вашей структуре 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)
@JoshAdel Я не понимаю вашей проблемы, просто создайте немаркированное перечисление play.rust-lang.org/…. Но ваш вопрос не касается вашей реальной проблемы. Например, если эти структуры определены где-то еще, почему они являются производными Serialize
и Deserialize
? Если у вас все еще есть проблема, задайте другой вопрос, который точно описывает ваш случай, или зайдите в чат ржавчины.
Что, если структуры
MessageOne
,MessageTwo
и т. д. Определены в стороннем ящике с полемop
? Можно ли создатьenum
, который собирает их вместе, а затем анализирует до нужного?