A rust crate helping you fortify your application against badly
formatted user input and supporting you with the integratation of
unintuitive and complex APIs.
This crate enhances serde's parsing capabilities,
focusing on the edge-cases of APIs that can't be integrated well with
rust's type system.
deserialize_custom_strings
offers functions that can be used with
serde's
deserialize_with
field attribute and helps you deal with three edge-cases:
-
Deserializing fields by converting them from a different type, for example parsing a string, converting its contents into an integer
-
Deserializing types that are encoded in a string, for example a json object encoded as a string in a yaml object
-
Strings that must adhere to a certain format, like email addresses, urls or phone numbers
- Deserializing fields by converting from different type
- Deserializing types encoded in a string
- Custom string deserializers
TODO: general description of what capabilities serde already offers and what this crates does to enhance these capabilities. Give real world example (u64 from string)
TODO: mention it as a an equivalent to container attribute from
for
fields
use serde::Deserialize;
use deserialize_custom_strings::convert::deserialize_from;
#[derive(Deserialize, Debug)]
struct Foo {
#[serde(deserialize_with = "deserialize_from::<_, bool, _>")]
bar: u8,
}
let json = r#"{
"bar": true
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, 1);
use serde::Deserialize;
use deserialize_custom_strings::convert::deserialize_try_from;
#[derive(Deserialize, Debug)]
struct Foo {
#[serde(deserialize_with = "deserialize_try_from::<_, i8, _>")]
bar: u8,
}
// This will parse `bar` as i8 (ranging from -128 to 127) before
// trying to convert the i8 value to u8 with the `TryFrom` trait.
// If the i8 is out of u8 range (0 to 255), parsing will fail.
let json = r#"{
"bar": 127
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, 127);
let json = r#"{
"bar": 127
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, 127);
// Fails, because 128 is not a value i8 covers
let json = r#"{
"bar": 128
}"#;
assert!(serde_json::from_str::<Foo>(json).is_err());
// Fails, because -1 is not convertible from i8 to u8
let json = r#"{
"bar": -1
}"#;
assert!(serde_json::from_str::<Foo>(json).is_err());
use serde::Deserialize;
use deserialize_custom_strings::convert::deserialize_from_str;
#[derive(Deserialize, Debug)]
struct Foo {
#[serde(deserialize_with = "deserialize_from_str")]
bar: u8,
}
let json = r#"{
"bar": "255"
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, 255);
TODO: link to
tracking issue for
specialization and describe why this make Option weak concerning the
implementation of conversion traits From
, TryFrom
and FromStr
.
use serde::Deserialize;
use deserialize_custom_strings::convert::{
deserialize_from_option,
deserialize_try_from_option,
deserialize_from_str_option,
};
#[derive(Deserialize, Debug)]
struct Foo {
#[serde(deserialize_with = "deserialize_from_option::<_, bool, _>")]
bar: Option<u8>,
#[serde(deserialize_with = "deserialize_try_from_option::<_, i8, _>")]
baz: Option<u8>,
#[serde(deserialize_with = "deserialize_from_str_option")]
bat: Option<u8>,
}
let json = r#"{
"bar": null,
"baz": null,
"bat": null
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, None);
assert_eq!(foo.baz, None);
assert_eq!(foo.bat, None);
let json = r#"{
"bar": true,
"baz": 127,
"bat": "255"
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, Some(1));
assert_eq!(foo.baz, Some(127));
assert_eq!(foo.bat, Some(255));
When deserializing json and you're using serde's deserialize_with
field attribute, optional fields can't be omitted from the json
object.
If an optional field is omitted from the json object, deserialization
will fail, unless you also provide the default
field attribute
(see related issues
1728 and
2249):
use serde::Deserialize;
use deserialize_custom_strings::convert::deserialize_from_option;
#[derive(Deserialize, Debug)]
struct Foo {
#[serde(deserialize_with = "deserialize_from_option::<_, bool, _>")]
bar: Option<u8>,
}
// Works, because the "bar" field is explicitly given
let json = r#"{
"bar": null
}"#;
let foo: Foo = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, None);
// Does not work, because "bar" field is omitted from the json
// object. If the `deserialize_with` field attribute wouldn't have
// been used, deserialization would work, but serde/serde_json is not
// able to handle omitted fields when `deserialize_with` is used
let json = "{}";
let err = serde_json::from_str::<Foo>(json);
assert!(err.is_err());
// Works, because of the `#[serde(default)]` tag for "bar"
#[derive(Deserialize, Debug)]
struct FooWithDefault {
#[serde(default)]
#[serde(deserialize_with = "deserialize_from_option::<_, bool, _>")]
bar: Option<u8>,
}
let json = "{}";
let foo: FooWithDefault = serde_json::from_str(json).unwrap();
assert_eq!(foo.bar, None);