Rust's standard library offers
traits your types can implement in order to serialize
(std::fmt::{Display, Debug}
) or deserialize (std::str::FromStr
)
them to and from strings.
Serialization and deserialization of data types in rust is usually
done by implementing the Serialize
and Deserialize
traits from
the serde crate.
display_json
is a crate that allows you to easily integrate
serde
's functionality with these traits from std
using custom
derive procedural macros.
These macros (de)serialize your types to and from
json strings by wrapping
serde_json's serialization and
deserialization capabilities into the traits from std
.
- Serializing objects to json with rust std's fmt traits
- Deserializing objects from json with rust std's FromStr trait
display_json
provides you with an easy way of combining the
serialization of your objects to json and the usage of rust's
built-in formatting features provided by the Display
and Debug
traits.
For string serialization to json, display_json
exposes the custom
derive procedural macros DisplayAsJson
, DisplayAsJsonPretty
,
DebugAsJson
and DebugAsJsonPretty
.
This allows you to use std
's
These custom derives create a stringified json version of an object
using serde_json.
The four custom derive macros are basically neat wrappers that wrap
the to_string
and to_string_pretty
functions from the
serde_json crate into an
implementation of Display
or Debug
for your type.
Without display_json
, you'd have to serialize your object to a json
string like this:
use serde::Serialize;
use serde_json::{Result, to_string};
#[derive(Serialize)]
struct Foo {
bar: String,
baz: i32,
bat: bool,
}
fn main() -> Result<()> {
let f = Foo { bar: "bar".to_owned(), baz: 0, bat: true };
let s = to_string(&f)?;
assert_eq!(s, r#"{"bar":"bar","baz":0,"bat":true}"#);
Ok(())
}
The same can be accomplished with less code by using the
DisplayAsJson
custom derive on Foo
:
use serde::Serialize;
use display_json::DisplayAsJson;
#[derive(Serialize, DisplayAsJson)]
struct Foo {
bar: String,
baz: i32,
bat: bool,
}
let f = Foo { bar: "bar".to_owned(), baz: 0, bat: true };
assert_eq!(f.to_string(), r#"{"bar":"bar","baz":0,"bat":true}"#);
DisplayAsJson
is a wrapper around serde_json::to_string
.
It takes the serialized json string and provides it to the Display
trait which is implemented by DisplayAsJson
.
This makes it more convenient to format your objects to json.
For example, you can use this to create well-ingestable log messages
or to serialize your data for sending it as the body of an http
request:
use serde::Serialize;
use display_json::DisplayAsJson;
#[derive(Serialize, DisplayAsJson)]
struct Foo {
bar: String,
baz: i32,
bat: bool,
}
let f = Foo { bar: "bar".to_owned(), baz: 0, bat: true };
// log `f` to stdout:
println!("{}", f);
// or you could construct an http request body from it
// or process your serialized object any other way you please
DebugAsJson
works the same as DisplayAsJson
, only instead of
implementing the Display
trait, it implements the Debug
trait:
use serde::Serialize;
use display_json::DebugAsJson;
#[derive(Serialize, DebugAsJson)]
struct Foo {
bar: String,
baz: i32,
bat: bool,
}
let f = Foo { bar: "bar".to_owned(), baz: 0, bat: true };
// note that we use the debug formatter for serializing `f` to json
let f_ser = format!("{:?}", f);
assert_eq!(f_ser, r#"{"bar":"bar","baz":0,"bat":true}"#);
DisplayAsJsonPretty
and DebugAsJsonPretty
work the same as their
non-pretty counterparts, except producing a multiline, indented json
string instead of a compact json string:
use serde::Serialize;
use display_json::{DisplayAsJsonPretty, DebugAsJsonPretty};
#[derive(Serialize, DisplayAsJsonPretty, DebugAsJsonPretty)]
struct Foo {
bar: String,
baz: i32,
bat: bool,
}
let f = Foo { bar: "bar".to_owned(), baz: 0, bat: true };
let result = r#"{
"bar": "bar",
"baz": 0,
"bat": true
}"#;
let f_ser = format!("{}", f);
let f_ser_dbg = format!("{:?}", f);
assert_eq!(f_ser, f_ser_dbg);
assert_eq!(f_ser, result);
assert_eq!(f_ser_dbg, result);
As you can see in the example above, you can combine the
DisplayAsJson
and DebugAsJson
variants however you like.
For example, you could use DisplayAsJson
to serialize your object
for the body of an http request and DebugAsJsonPretty
for creating
well-readable debugging messages for you to debug your code.
While deserialization of json strings is usually done by integrating
your program directly with the serde and
serde_json crates, sometimes you
want to or have to use rust's standard library's trait
std::str::FromStr
, which is how the types in std
implement
deserialization from string.
This could be due to interoperability constraints with other crate's
that perform deserialization from strings, without serde
integration.
An example of such a crate would be
clap's derive api.
If you find yourself confronted with the fact that your type needs
to implement std::str::FromStr
, even though it implements
serde::Deserialize
and you have no intention of thinking up a
custom format with a parser just to cumbersomely write an
implementation for FromStr
by hand, display_json
is a great
choice.
display_json
exposes the FromStrAsJson
custom derive procedural
macro you can derive on your type.
FromStrAsJson
implements std::str::FromStr
as a wrapper around
serde_json::from_str
.
If you need to implement the FromStr
trait for your type and all
you want to do is to deserialize a json string with it,
FromStrAsJson
is the solution for you with the least amount of
code, making sure your focus lies on what your program does, without
you having to stare at boilerplate code.
FromStrAsJson
is used as follows:
use serde::Deserialize;
use display_json::FromStrAsJson;
use std::str::FromStr;
#[derive(Deserialize, FromStrAsJson, PartialEq, Debug)]
struct Foo {
bar: String,
baz: i32,
bat: bool,
}
let f_as_json = r#"{"bar":"bar","baz":0,"bat":true}"#;
let f = Foo { bar: "bar".to_owned(), baz: 0, bat: true };
assert_eq!(Foo::from_str(f_as_json).unwrap(), f);