反序列化值向量,其中一个值是带有Serde的enum标记



我试图以一种具有挑战性的方式处理来自WebSocket的JSON格式,以便Serde进行反序列化。这个API有很多可能的响应,但在顶层,它总是至少有三个值。第一个是某个ID,第二个是作为对象的数据,第三个是对象的类型。

下面是一些JSON示例

[
32,
{
"speed": 900,
"altitude": 30000
"num_passengers": 200
},
"plane"
]

[
42,
{
"num_ingredients": 12,
"cook_time": "4 mins",
"oven_temp": 180
},
{
"num_ingredients": 4,
"cook_time": "25 mins",
"oven_temp": 250
},
"recipe"
]

我希望能够将其反序列化为enum。

enum Messages {
Plane(Plane),
Recipe(Recipe),
}

实际上,消息类型远远不止两种(大约有20种),我预计会收到相当大量的消息。由于这个原因,我有点担心使用无标记enum的性能。是否有其他解决方案来使用这种结构反序列化数据?

这是我想到的解决方案。

将目标结构定义为normal

#[derive(Debug, Serialize, Deserialize)]
struct Plane {
speed: u32,
altitude: i32,
num_passengers: u16
}
#[derive(Debug, Serialize, Deserialize)]
struct Recipe {
num_ingredients: u32,
cook_time: String,
oven_temp: u16
}
type Recipes = Vec<Recipe>;

则需要两个枚举。一个用来保存数据,另一个用来描述类型。注意到保存数据的一个有一个辅助函数(new)。这将创建自身的一个实例基于类型定义和JSON值

#[derive(Debug, Serialize, Deserialize, EnumDisplay)]
#[serde(rename_all = "camelCase")]
enum MessageType {
Plane,
Recipe
}
#[derive(Debug, Serialize)]
enum Message {
Plane(Plane),
Recipe(Recipes)
}
impl Message {
fn new(message_type: &MessageType, message: serde_json::Value) -> Result<Message, serde_json::Error> {
Ok(match message_type {
MessageType::Plane => Self::Plane(serde_json::from_value(message)?),
MessageType::Recipe => Self::Recipe(serde_json::from_value(message)?),
})
}
}

最后一部分是包装器结构体。这将保存消息id和数据。这是需要自定义反序列化器的结构体,所以不要派生Deserialize

#[derive(Debug, Serialize)]
struct MessageWrapper {
id: i64,
message: Message,
}

现在是有趣的部分。自定义反序列化实现。

一步一步,这是它的工作。

  1. 解析第一个值(id)作为i64
  2. 循环遍历下一个值,直到找到消息类型
    1. 如果值是字符串,则必须是消息类型,并且可以停止循环
    2. 如果值是一个对象或数组,将其添加到我们的消息列表
  3. 将数组值转换为单个值
    1. 如果数组中只有一个元素,删除它,那就是值
    2. 如果有多个内容,则使用值数组
    3. 构造一个新的Array类型值
  4. 使用我们在Messsage类型上创建的new函数来解析消息
  5. 构造并返回一个新的MessageWrapper
impl<'de> Deserialize<'de> for MessageWrapper {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MessageWrapperVisitor {}
impl<'de> Visitor<'de> for MessageWrapperVisitor {
type Value = MessageWrapper;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("[i64, Message..., String]")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let id: i64 = seq
.next_element()?
.ok_or_else(|| DeError::invalid_length(0, &self))?;
let message_type: MessageType;
let mut messages = Vec::with_capacity(1);
loop {
let val: serde_json::Value = seq
.next_element()?
.ok_or_else(|| DeError::invalid_length(1, &self))?;
if val.is_string() {
message_type = serde_json::from_value(val).or_else(|e| Err(DeError::custom(e)))?;
break;
} else if val.is_object() || val.is_array() {
messages.push(val);
} else {
return Err(DeError::custom("unexpected value. Expected channel name or json object"));
}
}
if messages.is_empty() {
return Err(DeError::custom("no data"));
}
let message: serde_json::Value;
if messages.len() == 1 {
message = messages.remove(0);
} else {
message = serde_json::Value::Array(messages)
}
let message = Message::new(&message_type, message)
.or_else(|e| Err(DeError::custom(format!("inner object cannot be deserialized as {} -> {}", message_type, e))))?;
Ok(MessageWrapper {
id,
message,
})
}
}
deserializer.deserialize_seq(MessageWrapperVisitor {})
}
}

最新更新