rust 如何反序列化Vec< SocketAddr>

ymdaylpp  于 2023-01-30  发布在  其他
关注(0)|答案(3)|浏览(107)

我目前将JSON数组反序列化为Vec<String>,并在应用程序的下游将单个String转换为SocketAddr
我想用serde将其反序列化为Vec<SocketAddr>

use serde::Deserialize;
use std::net::SocketAddr;

#[derive(Debug, Deserialize)]
struct Doc {
    // It would be nice to have Vec<SocketAddr> instead
    hosts: Vec<String>
}

fn main(){
    let data = r#"
        {"hosts": ["localhost:8000","localhost:8001"]}
    "#;
    let doc: Doc = serde_json::from_str(data).unwrap();
    dbg!(doc);
}
31moq8wy

31moq8wy1#

我不同意另外两个答案:你完全可以将"localhost:80"这样的字符串反序列化为SocketAddr(Vec of),但是你绝对不应该这样做,让我来解释一下:
您的问题是SocketAddr只包含一个IP地址+端口,而不是主机名。您可以通过将主机名解析为SockAddrToSocketAddrs来解决这个问题(然后将结果扁平化,因为一个主机名可以解析为多个地址):

#[derive(Debug, Deserialize)]
struct Doc {
    #[serde(deserialize_with = "flatten_resolve_addrs")]
    hosts: Vec<SocketAddr>,
}

fn flatten_resolve_addrs<'de, D>(de: D) -> Result<Vec<SocketAddr>, D::Error>
where
    D: Deserializer<'de>,
{
    // Being a little lazy here about allocations and error handling.
    // Because again, you shouldn't do this.
    let unresolved = Vec::<String>::deserialize(de)?;
    let mut resolved = vec![];
    for a in unresolved {
        let a = a
            .to_socket_addrs()
            .map_err(|e| serde::de::Error::custom(e))?;
        for a in a {
            resolved.push(a);
        }
    }
    Ok(resolved)
}

Playground
你不应该这样做,因为反序列化应该在序列化中来回,并且是输入字节的纯函数,但是解析地址可能会访问网络。

  • 当解析失败时,重试逻辑是什么?
  • 如果你的代码是长期运行的,解析结果可能会改变(从dns负载平衡,动态dns,网络配置更改,...),但你不能重新解析地址。
  • 如果您的代码作为系统服务启动,则在网络尚未完全配置的情况下,它可能无法启动,并出现反序列化错误。
  • 如果到指定地址之一的连接失败,则无法打印包含目标主机名的漂亮错误消息。
  • 您将如何为此配置默认端口?

(做错这些事情是我的一个小毛病,你会在nginx,Kubernetes,Zookeeper,......中找到。)
就我个人而言,出于简单性的原因,我可能会保留Vec<String>,但您也可以选择执行一些操作,如反序列化为Vec<(Either<IpAddr, Box<str>>, Option<u16>)>,以便检查字符串是否为有效地址,但要执行一些操作,如主机名解析和在连接到这些地址时提供默认端口。

jei2mxaa

jei2mxaa2#

Serde支持反序列化到SocketAddr,但无法解析您的输入,因为localhost不是有效的IPv4或IPv6地址。
输入需要是一个有效的IPv4或IPv6地址,以便直接反序列化为SocketAddr。如果您可以控制输入数据,则可以执行以下操作:

use serde::Deserialize;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs};

#[derive(Debug, Deserialize)]
struct Doc {
    hosts: Vec<SocketAddr>,
}

fn main() {
    let data = r#"
        {"hosts": ["127.0.0.1:8000","127.0.0.1:8001"]}
    "#;
    let doc: Doc = serde_json::from_str(data).unwrap();
    dbg!(doc);
}

下面是输出:

[src/main.rs:15] doc = Doc {
    hosts: [
        127.0.0.1:8000,
        127.0.0.1:8001,
    ],
}
brccelvz

brccelvz3#

您可以直接反序列化为SocketAddr,不需要额外的工作:

use serde::Deserialize;
use std::net::SocketAddr;

#[derive(Debug, Deserialize)]
struct Doc {
    // It would be nice to have Vec<SocketAddr> instead
    hosts: Vec<SocketAddr>,
}

fn main() {
    let data = r#"
        {"hosts": ["172.0.0.1:8000"]}
    "#;
    let doc: Doc = serde_json::from_str(data).unwrap();
    dbg!(doc);
}
[src/main.rs:15] doc = Doc {
    hosts: [
        172.0.0.1:8000,
    ],
}

您的问题是"localhost"不是有效的SocketAddr

相关问题