mitsuhiko / deser

Experimental rust serialization library
https://docs.rs/deser
Apache License 2.0
287 stars 8 forks source link

Numbers in JSON Serializer Keys #39

Open mitsuhiko opened 2 years ago

mitsuhiko commented 2 years ago

It's not clear right now how numbers as key in JSON are best to be supported. Right now this would fail as the number serializer and deserializer does not accept numbers encoded in strings. However that's effectively what one would need to do to support integers as hash maps.

This problem is somewhat tricky because there is no way right not to customize behavior within nested structures. The only customization is really only available on the level of the derive. So this hypothetical example does not work:

#[derive(Serialize, Deserialize)]
pub struct MyThing {
    map: HashMap<#[deser(as_string)] u32, bool>,
}

One hypothetical option would be to make the concept of "funnel through string" a property of the serialization system. In that case the u32 serializer and deserializer could probe the state to figure out if the current context requires supporting deserializing from a string. Something like this:

impl Sink for SlotWrapper<u32> {
    fn atom(&mut self, atom: Atom, state: &DeserializerState) -> Result<(), Error> {
        match atom {
            Atom::Str(s) if state.uses_string_tunneling() => {
                if let Ok(value) = s.parse() {
                    **self = Some(value);
                } else {
                    Err(Error::new(
                        ErrorKind::Unexpected,
                        "invalid value for number",
                    ))
                }
            }
            Atom::U64(value) => {
                let truncated = value as u32;
                if truncated as u64 == value {
                    **self = Some(truncated);
                    Ok(())
                } else {
                    Err(Error::new(
                        ErrorKind::OutOfRange,
                        "value out of range for type",
                    ))
                }
            }
            Atom::I64(value) => {
                let truncated = value as u32;
                if truncated as i64 == value {
                    **self = Some(truncated);
                    Ok(())
                } else {
                    Err(Error::new(
                        ErrorKind::OutOfRange,
                        "value out of range for type",
                    ))
                }
            }
            other => self.unexpected_atom(other, state),
        }
    }
}