I have a tricky situation where I have a collection of key-value pairs, and I need a custom serde Deserializer (custom data format in serde documentation) that can map those pairs over a struct.
The key-value pairs look something like this:
("device:address:ip", "127.0.0.1"),
("device:address:port", "9001"),
("device:keys:0", "0000"),
("device:keys:1", "1111"),
And the idea is to be able to deserialize them over top of a data model such as this one:
struct DeviceAddress {
ip: String,
port: u16
}
struct DeviceConfig {
address: DeviceAddress
keys: Vec<String>
}
struct Config {
device: DeviceConfig
}
In that scenario, the goal is to deserialize a HashMap<String, String> of such key-value pairs onto the Config struct. Instead of a &str input, I am looking at a **HashMap<String, String> ** as input to the deserializer.
To be as clear as possible, as serde walks the data model:
I need to construct this key:
device:address:ip
After which I can lookup the value in the HashMap of key-value pairs to resolve deserialization in step 7 (resolving value of the struct field).
I know the approach I need to take to do this; however, where I am stuck is within the deserializer, how do I keep track of the path as the serde deserialization infrastructure walks over the data model?
I found a pathway to solving this deserialization problem by thinking more about how serde walks the data structure. Serde provides information about structs and their fields that a deserializer can remember while being walked by the serde deserialization infrastructure.
For this particular deserialization implementation, the idea is to build up a key path as the deserializer walks over the data model, and then resolve a value from that key path when reaching a leaf, such as the value to be emitted for a struct field.
It looks like this:
struct ConfigMap<'a>(HashMap<&'a str, &'a str>);
struct Deserializer<'a> {
config: &'a ConfigMap<'a>,
fields: Vec<&'static str>,
key: Vec<String>
}
impl <'a> Deserializer<'a> {
pub fn new(config: &'a ConfigMap<'a>) -> Self {
Deserializer {
config,
fields: vec![],
key: vec![]
}
}
pub fn read<T: FromStr>(&mut self) -> Option<T> {
let key = self.key.join(":");
self.key.pop();
self.config.0
.get(key.as_str())
.and_then(|v| v.parse().ok())
}
}
impl <'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = serde::de::value::Error;
fn deserialize_any<V>(self, _: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
Err(serde::de::Error::custom(
"Deserialization to target type is unsupported"))
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<bool>().unwrap_or_default();
visitor.visit_bool(value)
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<i8>().unwrap_or_default();
visitor.visit_i8(value)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<i16>().unwrap_or_default();
visitor.visit_i16(value)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<i32>().unwrap_or_default();
visitor.visit_i32(value)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<i64>().unwrap_or_default();
visitor.visit_i64(value)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<u8>().unwrap_or_default();
visitor.visit_u8(value)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<u16>().unwrap_or_default();
visitor.visit_u16(value)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<u32>().unwrap_or_default();
visitor.visit_u32(value)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<u64>().unwrap_or_default();
visitor.visit_u64(value)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<f32>().unwrap_or_default();
visitor.visit_f32(value)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<f64>().unwrap_or_default();
visitor.visit_f64(value)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<char>().unwrap_or_default();
visitor.visit_char(value)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
let value = self.read::<String>();
visitor.visit_string(value.unwrap_or_default())
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
visitor.visit_map(self)
}
fn deserialize_struct<V>(self, name: &'static str, fields: &'static [&'static str], visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
self.fields.clear();
for field in fields.iter() {
self.fields.push(field);
}
self.deserialize_map(visitor)
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>
{
if let Some(field) = self.fields.pop() {
self.key.push(field.to_string());
let value = visitor.visit_str(field);
return value;
}
Err(serde::de::Error::custom("invalid field"))
}
forward_to_deserialize_any! {
enum ignored_any tuple tuple_struct seq
newtype_struct unit_struct unit option
byte_buf bytes str
}
}
impl <'de, 'a> MapAccess<'de> for Deserializer<'de> {
type Error = serde::de::value::Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: DeserializeSeed<'de>
{
if self.fields.len() > 0 {
seed.deserialize(&mut *self).map(Some)
} else {
self.key.pop();
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'de>
{
seed.deserialize(&mut *self)
}
}
This serializer is now capable of deserializing this example:
#[derive(Debug, Default, Deserialize)]
pub struct Address {
pub ip: String,
pub port: u16
}
#[derive(Debug, Default, Deserialize)]
pub struct Device {
pub address: Address,
pub enabled: bool
}
#[derive(Debug, Default, Deserialize)]
pub struct Config {
pub device: Device
}
let mut config = HashMap::new();
config.insert("device:address:ip", "127.0.0.1");
config.insert("device:address:port", "9206");
config.insert("device:enabled", "true");
let map = ConfigMap(config);
let mut deserializer = Deserializer::new(&map);
let config: Config = Config::deserialize(&mut deserializer).expect("failed to parse");
println!("{:?}", config);
// Config { device: Device { address: Address { ip: "127.0.0.1", port: 9206 }, enabled: true } }