rust 如何支持Serde枚举的未知值或其他值?

wkftcu5l  于 2024-01-08  发布在  其他
关注(0)|答案(5)|浏览(202)

我有一个JSON API,它返回一个看起来像这样的对象:

  1. {
  2. "PrivatePort": 2222,
  3. "PublicPort": 3333,
  4. "Type": "tcp"
  5. }

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

  1. #[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
  2. #[serde(rename_all = "snake_case")]
  3. pub enum PortType {
  4. Sctp,
  5. Tcp,
  6. Udp,
  7. }
  8. #[derive(Deserialize, Serialize, Debug)]
  9. #[serde(rename_all = "PascalCase")]
  10. pub struct PortMapping {
  11. pub private_port: u16,
  12. pub public_port: u16,
  13. #[serde(rename = "Type")]
  14. pub port_type: PortType,
  15. }


目前,这个API只支持PortType中列出的三个协议,但让我们假设将来会添加对DCCP的支持。我不希望API的客户端仅仅因为配置选项中的未知字符串而开始失败。
为了解决这个问题,我添加了一个Unknown变量,用String来表示值:

  1. #[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
  2. #[serde(rename_all = "snake_case")]
  3. pub enum PortType {
  4. Sctp,
  5. Tcp,
  6. Udp,
  7. Unknown(String),
  8. }


这里的目标是在传入一个未知值时,最终得到一个稍微不方便的PortType::Unknown("dccp")值。当然,这并不像我希望的那样开箱即用--传入未知的"dccp"值将导致:

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


是否有一个Serde配置来做我想要的事情,或者我应该手动编写PortTypeDeserializeSerialize实现?

n8ghc7c1

n8ghc7c11#

尝试使用serde-enum-str

  1. #[derive(serde_enum_str::Deserialize_enum_str, serde_enum_str::Serialize_enum_str)]
  2. #[serde(rename_all = "snake_case")]
  3. pub enum PortType {
  4. Sctp,
  5. Tcp,
  6. Udp,
  7. #[serde(other)]
  8. Unknown(String),
  9. }

字符串

2ekbmq32

2ekbmq322#

这是一个问题,虽然它已经开放了3年,到目前为止还没有完全解决。
在这篇文章发表的时候,目前似乎已经实现了#[serde(other)](尽管没有文档)。它只能应用于单元枚举字段,这限制了它的实用性:

  1. #[derive(Deserialize, PartialEq)]
  2. #[serde(tag = "tag")]
  3. enum Target {
  4. A(()),
  5. B(()),
  6. #[serde(other)]
  7. Others
  8. }
  9. fn main() {
  10. assert_eq!(Target::Others, from_str::<Target>(r#"{ "tag": "blablah" }"#).unwrap());
  11. }

字符串
除此之外,在撰写本文时,唯一的其他方法就是编写自己的Deserialize实现。

展开查看全部
pokxtpni

pokxtpni3#

我用serde(from=“String”)来做

  1. #[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
  2. #[serde(rename_all = "snake_case", from="String")]
  3. pub enum PortType {
  4. Sctp,
  5. Tcp,
  6. Udp,
  7. Unknown(String),
  8. }
  9. impl From<String> for PortType {
  10. fn from(s: String)->Self {
  11. use PortType::*;
  12. return match s.as_str() {
  13. "sctp" => Sctp,
  14. "tcp" => Tcp,
  15. "udp" => Udp,
  16. _ => Unknown(s)
  17. }
  18. }
  19. }
  20. #[derive(Deserialize, Serialize, Debug)]
  21. #[serde(rename_all = "PascalCase")]
  22. pub struct PortMapping {
  23. pub private_port: u16,
  24. pub public_port: u16,
  25. #[serde(rename = "Type")]
  26. pub port_type: PortType,
  27. }

字符串

展开查看全部
cld4siwp

cld4siwp4#

简单的情况下应该是罚款与此:

  1. use serde::de::Visitor;
  2. use serde::{Deserialize, Deserializer, Serialize};
  3. use serde_json::from_str;
  4. #[derive(Deserialize, Serialize, Debug)]
  5. #[serde(rename_all = "PascalCase")]
  6. pub struct PortMapping {
  7. pub private_port: u16,
  8. pub public_port: u16,
  9. #[serde(rename = "Type")]
  10. pub port_type: PortType,
  11. }
  12. #[derive(Clone, Eq, PartialEq, Serialize, Debug)]
  13. pub enum PortType {
  14. Sctp,
  15. Tcp,
  16. Udp,
  17. Unknown(String),
  18. }
  19. const PORT_TYPE: &'static [(&'static str, PortType)] = &[
  20. ("sctp", PortType::Sctp),
  21. ("tcp", PortType::Tcp),
  22. ("udp", PortType::Udp),
  23. ];
  24. impl From<String> for PortType {
  25. fn from(variant: String) -> Self {
  26. PORT_TYPE
  27. .iter()
  28. .find(|(id, _)| *id == &*variant)
  29. .map(|(_, port_type)| port_type.clone())
  30. .unwrap_or(PortType::Unknown(variant))
  31. }
  32. }
  33. impl<'a> From<&'a str> for PortType {
  34. fn from(variant: &'a str) -> Self {
  35. PORT_TYPE
  36. .iter()
  37. .find(|(id, _)| *id == &*variant)
  38. .map(|(_, port_type)| port_type.clone())
  39. .unwrap_or_else(|| PortType::Unknown(variant.to_string()))
  40. }
  41. }
  42. impl<'de> Deserialize<'de> for PortType {
  43. fn deserialize<D>(de: D) -> Result<PortType, D::Error>
  44. where
  45. D: Deserializer<'de>,
  46. {
  47. struct PortTypeVisitor {}
  48. impl<'de> Visitor<'de> for PortTypeVisitor {
  49. type Value = PortType;
  50. fn expecting(
  51. &self,
  52. fmt: &mut std::fmt::Formatter<'_>,
  53. ) -> std::result::Result<(), std::fmt::Error> {
  54. fmt.write_str("We expected a string")
  55. }
  56. fn visit_str<E>(self, variant: &str) -> Result<Self::Value, E> {
  57. Ok(variant.into())
  58. }
  59. fn visit_string<E>(self, variant: String) -> Result<Self::Value, E> {
  60. Ok(variant.into())
  61. }
  62. }
  63. de.deserialize_string(PortTypeVisitor {})
  64. }
  65. }
  66. fn main() {
  67. let input = r#"
  68. {
  69. "PrivatePort": 2222,
  70. "PublicPort": 3333,
  71. "Type": "dccp"
  72. }
  73. "#;
  74. let result: Result<PortMapping, _> = from_str(input);
  75. println!("{:#?}", result);
  76. }

字符串
我不认为有一个习惯的方式来做到这一点,这可以包括在未来。

展开查看全部
pftdvrlh

pftdvrlh5#

您现在可以在变量级别上使用#[serde(untagged)]属性来捕获未知变量。

  1. #[derive(Eq, PartialEq, Deserialize, Serialize, Debug)]
  2. #[serde(rename_all = "snake_case")]
  3. pub enum PortType {
  4. Sctp,
  5. Tcp,
  6. Udp,
  7. #[serde(untagged)]
  8. Unknown(String),
  9. }

字符串
不要与枚举级别上的未标记属性混淆。
我找不到这个属性的任何文档。
感谢https://github.com/serde-rs/serde/issues/912#issuecomment-1868785603

相关问题