我如何支持 Serde 枚举的未知值或其他值? [英] How can I support an unknown or other value for a Serde enum?

查看:53
本文介绍了我如何支持 Serde 枚举的未知值或其他值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 JSON API,它返回一个如下所示的对象:

I have a JSON API that returns an object that looks like this:

{
  "PrivatePort": 2222,
  "PublicPort": 3333,
  "Type": "tcp"
}

为了捕获这个,我有一个枚举和一个结构:

To capture this, I have an enum and a struct:

#[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PortMapping {
    pub private_port: u16,
    pub public_port: u16,
    #[serde(rename = "Type")]
    pub port_type: PortType,
}

目前,此 API 仅支持 PortType 中列出的三个协议,但我们假设将来会添加对 DCCP 的支持.我不希望 API 的客户端仅仅因为他们可能没有查看的配置选项中的未知字符串而开始失败.

Right now, this API only supports the three protocols listed in PortType, but let's assume that support for DCCP is added in the future. I do not want clients of the API to start failing simply because of an unknown string in a configuration option they might not be looking at.

为了解决这个问题,我添加了一个带有 StringUnknown 变体来表示值:

To address this, I've added an Unknown variant with a String to represent the value:

#[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
#[serde(rename_all = "snake_case")]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
    Unknown(String),
}

这里的目标是在传入未知值时以稍微不方便的 PortType::Unknown("dccp") 值结束.当然,这不会做我想做的就像开箱即用——传递未知的 "dccp" 值将导致:

The goal here is to end up with the slightly-inconvenient PortType::Unknown("dccp") value when an unknown value is passed in. Of course, this does not do what I would like out-of-box -- passing the unknown "dccp" value will result in:

Error("unknown variant `dccp`, expected one of `sctp`, `tcp`, `udp`, `unknown`", line: 1, column: 55)

是否有 Serde 配置来做我想做的事,还是我应该手动编写 DeserializeSerialize 实现 PortType?>

Is there a Serde configuration for doing what I want or should I resort to manually writing Deserialize and Serialize implementations for PortType?

推荐答案

简单的案例应该没问题:

Simple case should be fine with this:

use serde::de::Visitor;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::from_str;

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PortMapping {
    pub private_port: u16,
    pub public_port: u16,
    #[serde(rename = "Type")]
    pub port_type: PortType,
}

#[derive(Clone, Eq, PartialEq, Serialize, Debug)]
pub enum PortType {
    Sctp,
    Tcp,
    Udp,
    Unknown(String),
}

const PORT_TYPE: &'static [(&'static str, PortType)] = &[
    ("sctp", PortType::Sctp),
    ("tcp", PortType::Tcp),
    ("udp", PortType::Udp),
];

impl From<String> for PortType {
    fn from(variant: String) -> Self {
        PORT_TYPE
            .iter()
            .find(|(id, _)| *id == &*variant)
            .map(|(_, port_type)| port_type.clone())
            .unwrap_or(PortType::Unknown(variant))
    }
}

impl<'a> From<&'a str> for PortType {
    fn from(variant: &'a str) -> Self {
        PORT_TYPE
            .iter()
            .find(|(id, _)| *id == &*variant)
            .map(|(_, port_type)| port_type.clone())
            .unwrap_or_else(|| PortType::Unknown(variant.to_string()))
    }
}

impl<'de> Deserialize<'de> for PortType {
    fn deserialize<D>(de: D) -> Result<PortType, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct PortTypeVisitor {}

        impl<'de> Visitor<'de> for PortTypeVisitor {
            type Value = PortType;

            fn expecting(
                &self,
                fmt: &mut std::fmt::Formatter<'_>,
            ) -> std::result::Result<(), std::fmt::Error> {
                fmt.write_str("We expected a string")
            }

            fn visit_str<E>(self, variant: &str) -> Result<Self::Value, E> {
                Ok(variant.into())
            }

            fn visit_string<E>(self, variant: String) -> Result<Self::Value, E> {
                Ok(variant.into())
            }
        }

        de.deserialize_string(PortTypeVisitor {})
    }
}

fn main() {
    let input = r#"
    {
      "PrivatePort": 2222,
      "PublicPort": 3333,
      "Type": "dccp"
    }
    "#;

    let result: Result<PortMapping, _> = from_str(input);

    println!("{:#?}", result);
}

我不认为有一种惯用的方法可以做到这一点,将来可能会包括在内.

I don't think there is a idiomatic way to do this, that could be included in the future.

这篇关于我如何支持 Serde 枚举的未知值或其他值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆