I currently deserialize a JSON array into Vec<String>
and down stream in my application I convert individual String
to SocketAddr
.
I would like to do the deserialiation into Vec<SocketAddr>
with serde instead.
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);
}
I'd like to disagree with the other two answers: You can absolutely deserialize strings like "localhost:80"
to a (Vec of) SocketAddr
. But you absolutely shouldn't. Let me explain:
Your problem is that SocketAddr
only holds an IP address + port, not hostnames. You can solve this by resolving hostnames into SockAddr
through ToSocketAddrs
(and then flattening the result because one hostname can resolve to multiple addrs):
#[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);
}
} panic!("You really shouldn't. Go read my Stackoverflow answer.");
Ok(resolved)
}
You shouldn't do this because, deserialization should really round-trip through serialization, and be a pure function of the input bytes, but resolving addresses may access the network. Problems are:
(Doing these things wrong is a pet peeve of mine, you'll find it in nginx, Kubernetes, Zookeeper, …)
Personally, I'd probably keep the Vec<String>
for simplicity reasons, but you might also choose to do something like deserializing to Vec<(Either<IpAddr, Box<str>>, Option<u16>)>
so you can check whether the strings are valid addresses, but do things like hostname resolution and providing a default port when you connect to those addresses.