How to use a custom serde deserializer for chrono timestamps? - rust

I'm trying to parse JSON into a struct which has a chrono::DateTime field. The JSON has the timestamps saved in a custom format that I wrote a deserializer for.
How do I connect the two and get it working using #[serde(deserialize_with)]?
I'm using NaiveDateTime for simpler code
extern crate serde;
extern crate serde_json;
use serde::Deserialize;
extern crate chrono;
use chrono::NaiveDateTime;
fn from_timestamp(time: &String) -> NaiveDateTime {
NaiveDateTime::parse_from_str(time, "%Y-%m-%dT%H:%M:%S.%f").unwrap()
}
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "from_timestamp")]
timestamp: NaiveDateTime,
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
I'm getting three different compile errors:
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^^ expected reference, found type parameter
|
= note: expected type `&std::string::String`
found type `__D`
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^-
| | |
| | this match expression has type `chrono::NaiveDateTime`
| expected struct `chrono::NaiveDateTime`, found enum `std::result::Result`
| in this macro invocation
|
= note: expected type `chrono::NaiveDateTime`
found type `std::result::Result<_, _>`
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^-
| | |
| | this match expression has type `chrono::NaiveDateTime`
| expected struct `chrono::NaiveDateTime`, found enum `std::result::Result`
| in this macro invocation
|
= note: expected type `chrono::NaiveDateTime`
found type `std::result::Result<_, _>`
I'm pretty sure the from_timestamp function is returning a DateTime struct and not a Result, so I don't know what "expected struct chrono::NaiveDateTime, found enum std::result::Result" may mean.

While #edwardw's answer is technically correct it IMHO contains too much boilerplate.
NaiveDataTime implements FromStr which means you can write a reusable generic deserializer function.
A convoluted example - did add the age field (u8) represented as string in the JSON. Just to demonstrate that you can use it for anything that implements FromStr.
use std::fmt::Display;
use std::str::FromStr;
use chrono::NaiveDateTime;
use serde::{de, Deserialize, Deserializer};
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "deserialize_from_str")]
timestamp: NaiveDateTime,
#[serde(deserialize_with = "deserialize_from_str")]
age: u8,
}
// You can use this deserializer for any type that implements FromStr
// and the FromStr::Err implements Display
fn deserialize_from_str<'de, S, D>(deserializer: D) -> Result<S, D::Error>
where
S: FromStr, // Required for S::from_str...
S::Err: Display, // Required for .map_err(de::Error::custom)
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
S::from_str(&s).map_err(de::Error::custom)
}
fn main() {
let result: MyJson = serde_json::from_str(
r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108", "age": "11"}"#,
)
.unwrap();
println!("{:?}", result);
}
It's even easier if you want to specify format (use NaiveDateTime::parse_from_str):
use chrono::NaiveDateTime;
use serde::{de, Deserialize, Deserializer};
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "naive_date_time_from_str")]
timestamp: NaiveDateTime,
}
fn naive_date_time_from_str<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S.%f").map_err(de::Error::custom)
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
#[serde(deserialize_with = "path")] documentation:
Deserialize this field using a function that is different from its implementation of Deserialize. The given function must be callable as fn<'de, D>(D) -> Result<T, D::Error> where D: Deserializer<'de>, although it may also be generic over T. Fields used with deserialize_with are not required to implement Deserialize.

This is rather involved, but the following works:
use chrono::NaiveDateTime;
use serde::de;
use serde::Deserialize;
use std::fmt;
struct NaiveDateTimeVisitor;
impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor {
type Value = NaiveDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a string represents chrono::NaiveDateTime")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%f") {
Ok(t) => Ok(t),
Err(_) => Err(de::Error::invalid_value(de::Unexpected::Str(s), &self)),
}
}
}
fn from_timestamp<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_str(NaiveDateTimeVisitor)
}
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "from_timestamp")]
timestamp: NaiveDateTime,
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}

Related

Wrapper over Borsh Serialization/Deserialization methods in Rust

I have a struct A which implements BorshDeserialize and BorshSerialize as follows
#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
struct A {
a : i32,
b: String,
}
I know I can serialize or deserialize A when I do the following:-
let s = A {a: 1 , b: "a".to_string()};
// Serialize
let serialzed_data = s.try_to_vec().unwrap()
// Deserialize
deserialized_struct = A::try_from_slice(&serialzed_data).unwrap();
I am trying to wrap over these two methods by creating two general traits on main.rs
where I import this struct from another file a.rs.
pub trait Serializable<T: BorshSerialize> {
fn serialize(s: T) -> Vec<u8> {
s.try_to_vec().unwrap()
}
}
pub trait Deserializable<T : BorshDeserialize> {
fn deserialize(s: &[u8]) -> Result<T, ()> {
let deserialized_val = match T::try_from_slice(&s) {
Ok(val) => {val},
Err(_) => {return Err(());},
};
Ok(deserialized_val)
}
}
where I implement Serialize and Deserialize for A as follows on a.rs
impl Serializable<A> for A {}
impl Deserializable<A> for A {}
But in the source code when I call the method serialize on A
for this instruction,
A::serialize(&some_instance_of_A).unwrap()
I get the following error
A::serialize(&some_instance_of_A).unwrap()
^^^^^^^^^ multiple `serialize` found
|
= note: candidate #1 is defined in an impl of the trait `Serializable` for the type `A`
= note: candidate #2 is defined in an impl of the trait `BorshSerialize` for the type `A`
help: disambiguate the associated function for candidate #1
|
46 | <&Self as A::Serializable>::serialize(&some_instance_of_A), // TODO. See issue #64
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: disambiguate the associated function for candidate #2
|
46 | <&A as BorshSerialize>::serialize(&some_instance_of_A).concat(), // TODO. See issue #64
|
I understand that the compiler gets confused by the two instances of serialization schemes created(one due to Borsh from derive macro and the other from Serialize trait on main.rs).
Is there a way to directly make the serialize call fall to BorshSerialize by default when A::serialize is called.
By preventing to import the BorshSerialize trait you can get rid of the error.
With only the Serializable trait in scope, only one method can be called:
#[derive(Clone, PartialEq, Eq, borsh::BorshSerialize, borsh::BorshDeserialize)]
struct A {
a : i32,
b: String,
}
impl Serializable for A {}
fn main() {
let a = A { a: 0, b: String::from("") };
let vec = a.serialize();
eprintln!("{:?}", vec);
}
pub trait Serializable: borsh::BorshSerialize {
fn serialize(&self) -> Vec<u8> {
self.try_to_vec().unwrap()
}
}
pub trait Deserializable<T : borsh::BorshDeserialize> {
fn deserialize(s: &[u8]) -> Result<T, ()> {
let deserialized_val = match T::try_from_slice(&s) {
Ok(val) => {val},
Err(_) => {return Err(());},
};
Ok(deserialized_val)
}
}

Custom diesel type

I'm trying to implement a custom Diesel type using ToSql/FromSql traits.
My code now looks like this:
use diesel::{
backend::Backend,
deserialize,
serialize,
sql_types::VarChar,
};
#[derive(AsExpression, FromSqlRow, Debug)]
#[sql_type = "VarChar"]
pub struct File {
id: String,
}
impl File {
pub fn new(id: String) -> Self {
Self { id }
}
}
impl<B: Backend> serialize::ToSql<VarChar, B> for File {
fn to_sql(&self, out: &mut serialize::Output<B>) -> serialize::Result {
<String as serialize::ToSql<VarChar, B>>::to_sql(&self.id, out)
}
}
impl<B: Backend<RawValue=[u8]>> deserialize::FromSql<VarChar, B> for File {
fn from_sql(bytes: Option<&B::RawValue>) -> deserialize::Result<Self> {
<String as deserialize::FromSql<VarChar, B>>::from_sql(bytes).map(|id| File::new(id))
}
}
When I try to compile it, I receive a bunch of errors.
A couple of them are related to inability to detect names.
error: cannot find derive macro `AsExpression` in this scope
--> src\file.rs:8:10
|
8 | #[derive(AsExpression, FromSqlRow, Debug)]
| ^^^^^^^^^^^^
|
= note: consider importing this derive macro:
diesel::AsExpression
error: cannot find derive macro `FromSqlRow` in this scope
--> src\file.rs:8:24
|
8 | #[derive(AsExpression, FromSqlRow, Debug)]
| ^^^^^^^^^^
|
= note: consider importing one of these items:
crate::file::deserialize::FromSqlRow
diesel::FromSqlRow
error: cannot find attribute `sql_type` in this scope
--> src\file.rs:9:3
|
9 | #[sql_type = "VarChar"]
| ^^^^^^^^
I thought the problem was because I hadn't added Diesel's prelude to my scope. But, unfortunately, when I do this, I encounter the same problem.
The second problem looks like this:
error[E0212]: cannot use the associated type of a trait with uninferred generic
parameters
--> src\file.rs:30:32
|
30 | fn from_sql(bytes: Option<&B::RawValue>) -> deserialize::Result<Self> {
| ^^^^^^^^^^^
|
help: use a fully qualified path with inferred lifetimes
|
30 | fn from_sql(bytes: Option<&<B as backend::private::HasRawValue<'_>>::Ra
wValue>) -> deserialize::Result<Self> {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The compiler requires a fully qualified syntax although each example of custom types shows almost the same code.
It is not clear to me what version of Diesel you are using; there were plenty of changes between v1.4.8 and v2.0.0 that are relevant here. It also seems like you are using aspects of each version, but for completeness' sake, I'll answer for both.
Diesel 1.4.8
Since this version was created back with Rust 2015 edition, it requires you to import macros like this at the root of your crate with #[macro_use] (as shown in the documentation for the table! macro for example).
The method ToSql::to_sql uses an additional W: std::io::Write type parameter that you omitted (maybe from looking at the newest version).
The complete example:
#[macro_use]
extern crate diesel;
use diesel::{backend::Backend, deserialize, serialize, sql_types::VarChar};
#[derive(AsExpression, FromSqlRow, Debug)]
#[sql_type = "VarChar"]
pub struct File {
id: String,
}
impl File {
pub fn new(id: String) -> Self {
Self { id }
}
}
impl<B: Backend> serialize::ToSql<VarChar, B> for File {
fn to_sql<W: std::io::Write>(&self, out: &mut serialize::Output<W, B>) -> serialize::Result {
<String as serialize::ToSql<VarChar, B>>::to_sql(&self.id, out)
}
}
impl<B: Backend<RawValue = [u8]>> deserialize::FromSql<VarChar, B> for File {
fn from_sql(bytes: Option<&B::RawValue>) -> deserialize::Result<Self> {
<String as deserialize::FromSql<VarChar, B>>::from_sql(bytes).map(|id| File::new(id))
}
}
Diesel 2.0.0
This version, the crate was updated to Rust 2018 edition, meaning you no longer needed to use #[macro_use] but you still have to import them as normal items.
The macro attributes all changes to be wrapped by diesel() to avoid inter-crate ambiguity. So #[sql_type = "VarChar"] was changed to #[diesel(sql_type = VarChar)].
The method FromSql::from_sql changed its parameter from Option<&B::RawValue> to RawValue<B>, and the ToSql::to_sql method sprouted some lifetime annotations. Both also needed an additional constraint since I don't think its guaranteed that a B: Backend has a String: ToSql/FromSql implementation.
The complete example:
use diesel::{
backend::{Backend, RawValue},
deserialize, serialize,
sql_types::VarChar,
AsExpression, FromSqlRow,
};
#[derive(AsExpression, FromSqlRow, Debug)]
#[diesel(sql_type = VarChar)]
pub struct File {
id: String,
}
impl File {
pub fn new(id: String) -> Self {
Self { id }
}
}
impl<B: Backend> serialize::ToSql<VarChar, B> for File
where
String: serialize::ToSql<VarChar, B>,
{
fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, B>) -> serialize::Result {
<String as serialize::ToSql<VarChar, B>>::to_sql(&self.id, out)
}
}
impl<B: Backend> deserialize::FromSql<VarChar, B> for File
where
String: deserialize::FromSql<VarChar, B>,
{
fn from_sql(bytes: RawValue<B>) -> deserialize::Result<Self> {
<String as deserialize::FromSql<VarChar, B>>::from_sql(bytes).map(|id| File::new(id))
}
}

How do I pass a type bounded by a trait to Serde's deserialize_with?

A type satisfying MyTrait is supposed to be passed to deserialize_data specified by deserialize_with. Here is my sample code:
use serde::{Deserialize, Deserializer}; // 1.0.117
use serde_json; // 1.0.59
type Item = Result<String, Box<dyn std::error::Error + Send + Sync>>;
pub trait MyTrait {
fn method(ind: &str) -> Item;
}
#[derive(Deserialize)]
pub struct S<T>
where
T: MyTrait + ?Sized, // intend to pass a type T satisfying `MyTrait` to function `deserialize_data`,
{
#[serde(deserialize_with = "deserialize_data")]
//#[serde(bound( deserialize = "T: MyTrait, for<'de2> T: Deserialize<'de2>" ))]
pub data: String,
}
fn deserialize_data<'de, D, T>(d: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let ind = <&str>::deserialize(d).unwrap();
match T::method(ind) {
Ok(data) => Ok(data),
Err(e) => Err(serde::de::Error::custom(format_args!("invalid type."))),
}
}
struct A;
impl MyTrait for A {
fn method(_ind: &str) -> Item {
// to make it simple, return constant
Ok("method".to_string())
}
}
fn main() {
let s = r#"{"data": "string"}"#;
let ob: S<A> = serde_json::from_str(s).unwrap();
}
The compiler complains:
error[E0392]: parameter `T` is never used
--> src/main.rs:10:14
|
10 | pub struct S<T>
| ^ unused parameter
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
I do use T, and PhantomData doesn't help much.
One of the obvious way may be using struct A and its implemented method as crate or something then importing them. This, unfortunately, doesn't apply to my case, so I seek to pass a struct type to deserialize_data and achieve that.
To get the code to compile, you need to:
use T in struct S<T>, for example with PhantomData.
explicitly pass T to deserialize_data using the turbofish operator ::<>.
add the appropriate trait bounds to the T generic type in deserialize_data(), such as MyTrait.
For example (playground):
#[derive(Deserialize)]
pub struct S<T>
where
T: MyTrait + ?Sized,
{
#[serde(deserialize_with = "deserialize_data::<_, T>")]
pub data: String,
marker: std::marker::PhantomData<T>,
}
fn deserialize_data<'de, D, T>(d: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
T: MyTrait + ?Sized,
{
// ...
}

How can I mass implement Deserialize for all types that implement a specific trait?

I am deserializing a YAML config file with Serde. For most structs I deserialize into, things are quite simple — there's a one-to-one relationship between the fields of the structs and the properties in my YAML file.
In a few cases, things are a bit more complicated. For these, the properties in the YAML file are better viewed as parameters to the constructor. The actual struct will have different fields, calculated from those.
For these cases, I have written separate config structs that I deserialize into. For simplicity, consider this silly example:
struct Message {
text: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
To have Serde do the conversion from MessageConfig to Message for me, I implemented Deserialize for Message:
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
where
D: Deserializer<'de>,
{
MessageConfig::deserialize(deserializer).map(|config| Message::from_config(config))
}
}
This works, but there would be a lot of copy pasting of the deserialization code involved if I were to do this for every struct, so I figured I should make a trait out of it:
use serde::{Deserialize, Deserializer};
use serde_json;
#[macro_use]
extern crate serde_derive;
trait Configurable {
type Config;
fn from_config(config: Self::Config) -> Self;
}
impl<'de, T, C> Deserialize<'de> for T
where
T: Configurable<Config = C>,
C: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
Self::Config::deserialize(deserializer).map(|config| Self::from_config(config))
}
}
struct Message {
text: String,
}
impl<'de> Configurable for Message {
type Config = MessageConfig;
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
However, the compiler is not happy about this:
error[E0119]: conflicting implementations of trait `_IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'_>` for type `std::boxed::Box<_>`:
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^
|
= note: conflicting implementation in crate `serde`:
- impl<'de, T> _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de> for std::boxed::Box<T>
where T: _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de>;
= note: downstream crates may implement trait `Configurable` for type `std::boxed::Box<_>`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
The error messages make little sense to me. What does Box have to do with anything? And is it somehow possible to make this trait work?
I am not sure if there's a way to define such a broad trait without causing conflicting implementations. What you could do is use a macro to avoid repetition:
use serde::{Deserialize, Deserializer};
use serde_json;
use serde_json::Error;
#[macro_use]
extern crate serde_derive;
struct Message {
text: String,
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
macro_rules! derive_configurable_serializer {
( $t:ident, $c:ident ) => {
impl<'de> Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
where
D: Deserializer<'de>,
{
$c::deserialize(deserializer).map(|config| $t::from_config(config))
}
}
};
}
derive_configurable_serializer!(Message, MessageConfig);
fn main() -> Result<(), Error> {
let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;
let p: Message = serde_json::from_str(data)?;
println!("Hello, {}!", p.text);
Ok(())
}

Deserialize a JSON string or array of strings into a Vec

I'm writing a crate that interfaces with a JSON web API. One endpoint usually returns responses of the form { "key": ["value1", "value2"] }, but sometimes there's only one value for the key, and the endpoint returns { "key": "value" } instead of { "key": ["value"] }
I wanted to write something generic for this that I could use with #[serde(deserialize_with)] like so:
#[derive(Deserialize)]
struct SomeStruct {
#[serde(deserialize_with = "deserialize_string_or_seq_string")]
field1: Vec<SomeStringNewType>,
#[serde(deserialize_with = "deserialize_string_or_seq_string")]
field2: Vec<SomeTypeWithCustomDeserializeFromStr>,
}
#[derive(Deserialize)]
struct SomeStringNewType(String);
struct SomeTypeWithCustomDeserializeFromStr(String);
impl ::serde::de::Deserialize for SomeTypeWithCustomDeserializeFromStr {
// Some custom implementation here
}
How can I write a deserialize_string_or_seq_string to be able to do this?
In case you want to deserialize a single string or a list of strings into the more general Vec<String> instead of a custom type, the following is a simpler solution for Serde 1.0:
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
use std::fmt;
use std::marker::PhantomData;
use serde::de;
use serde::de::{Deserialize, Deserializer};
#[derive(Deserialize, Debug, Clone)]
pub struct Parent {
#[serde(deserialize_with = "string_or_seq_string")]
pub strings: Vec<String>,
}
fn main() {
let list_of_strings: Parent = serde_json::from_str(r#"{ "strings": ["value1", "value2"] }"#).unwrap();
println!("list of strings: {:?}", list_of_strings);
// Prints:
// list of strings: Parent { strings: ["value1", "value2"] }
let single_string: Parent = serde_json::from_str(r#"{ "strings": "value" }"#).unwrap();
println!("single string: {:?}", single_string);
// Prints:
// single string: Parent { strings: ["value"] }
}
fn string_or_seq_string<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where D: Deserializer<'de>
{
struct StringOrVec(PhantomData<Vec<String>>);
impl<'de> de::Visitor<'de> for StringOrVec {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or list of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: de::Error
{
Ok(vec![value.to_owned()])
}
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
where S: de::SeqAccess<'de>
{
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor))
}
}
deserializer.deserialize_any(StringOrVec(PhantomData))
}
This solution also works under the 0.9 release of Serde with the following changes:
remove the lifetimes
SeqAccess -> SeqVisitor
SeqAccessDeserializer -> SeqVisitorDeserializer
MapAccess -> MapVisitor
MapAccessDeserializer -> MapVisitorDeserializer
This solution works for Serde 1.0.
The way I found also required me to write a custom deserializer, because I needed one that would call visitor.visit_newtype_struct to try deserializing newtypes, and there don't seem to be any in-built into serde that do so. (I was expecting something like the ValueDeserializer series of types.)
A self-contained example is below. The SomeStruct is deserialized correctly for both inputs, one where the values are JSON arrays of strings, and the other where they're just strings.
#[macro_use]
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
fn main() {
#[derive(Debug, Deserialize)]
struct SomeStringNewType(String);
#[derive(Debug)]
struct SomeTypeWithCustomDeserializeFromStr(String);
impl<'de> ::serde::Deserialize<'de> for SomeTypeWithCustomDeserializeFromStr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: ::serde::Deserializer<'de> {
struct Visitor;
impl<'de> ::serde::de::Visitor<'de> for Visitor {
type Value = SomeTypeWithCustomDeserializeFromStr;
fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "a string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: ::serde::de::Error {
Ok(SomeTypeWithCustomDeserializeFromStr(v.to_string() + " custom"))
}
}
deserializer.deserialize_any(Visitor)
}
}
#[derive(Debug, Deserialize)]
struct SomeStruct {
#[serde(deserialize_with = "deserialize_string_or_seq_string")]
field1: Vec<SomeStringNewType>,
#[serde(deserialize_with = "deserialize_string_or_seq_string")]
field2: Vec<SomeTypeWithCustomDeserializeFromStr>,
}
let x: SomeStruct = ::serde_json::from_str(r#"{ "field1": ["a"], "field2": ["b"] }"#).unwrap();
println!("{:?}", x);
assert_eq!(x.field1[0].0, "a");
assert_eq!(x.field2[0].0, "b custom");
let x: SomeStruct = ::serde_json::from_str(r#"{ "field1": "c", "field2": "d" }"#).unwrap();
println!("{:?}", x);
assert_eq!(x.field1[0].0, "c");
assert_eq!(x.field2[0].0, "d custom");
}
/// Deserializes a string or a sequence of strings into a vector of the target type.
pub fn deserialize_string_or_seq_string<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
where T: ::serde::Deserialize<'de>, D: ::serde::Deserializer<'de> {
struct Visitor<T>(::std::marker::PhantomData<T>);
impl<'de, T> ::serde::de::Visitor<'de> for Visitor<T>
where T: ::serde::Deserialize<'de> {
type Value = Vec<T>;
fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "a string or sequence of strings")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where E: ::serde::de::Error {
let value = {
// Try parsing as a newtype
let deserializer = StringNewTypeStructDeserializer(v, ::std::marker::PhantomData);
::serde::Deserialize::deserialize(deserializer)
}.or_else(|_: E| {
// Try parsing as a str
let deserializer = ::serde::de::IntoDeserializer::into_deserializer(v);
::serde::Deserialize::deserialize(deserializer)
})?;
Ok(vec![value])
}
fn visit_seq<A>(self, visitor: A) -> Result<Self::Value, A::Error>
where A: ::serde::de::SeqAccess<'de> {
::serde::Deserialize::deserialize(::serde::de::value::SeqAccessDeserializer::new(visitor))
}
}
deserializer.deserialize_any(Visitor(::std::marker::PhantomData))
}
// Tries to deserialize the given string as a newtype
struct StringNewTypeStructDeserializer<'a, E>(&'a str, ::std::marker::PhantomData<E>);
impl<'de, 'a, E> ::serde::Deserializer<'de> for StringNewTypeStructDeserializer<'a, E> where E: ::serde::de::Error {
type Error = E;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: ::serde::de::Visitor<'de> {
visitor.visit_newtype_struct(self)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: ::serde::de::Visitor<'de> {
// Called by newtype visitor
visitor.visit_str(self.0)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str bytes
byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map
struct enum identifier ignored_any
}
}
I found this pattern to work for me in a similar situation:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum ParameterValue {
Primitive(String),
List(Vec<String>),
}
#[derive(Debug, Serialize, Deserialize)]
struct Parameter {
name: String,
value: ParameterValue,
}
example primitive:
let primitive = Parameter {
name: String::from("theKey"),
value: ParameterValue::Primitive(String::from("theValue")),
};
let primitive_serialized = serde_json::to_string(&primitive).unwrap();
println!("{primitive_serialized}");
let primitive_again: Parameter = serde_json::from_str(&primitive_serialized).unwrap();
println!("{primitive_again:?}");
Prints:
{"name":"theKey","value":"theValue"}
Parameter { name: "theKey", value: Primitive("theValue") }
example array:
let list = Parameter {
name: String::from("theKey"),
value: ParameterValue::List(vec![String::from("v1"), String::from("v2")]),
};
let list_serialized = serde_json::to_string(&list).unwrap();
println!("{list_serialized}");
let list_again: Parameter = serde_json::from_str(&list_serialized).unwrap();
println!("{list_again:?}");
Prints:
{"name":"theKey","value":["v1","v2"]}
Parameter { name: "theKey", value: List(["v1", "v2"]) }

Resources