SA 155FB3DF9D998E6BBBBBBBBBBBBBBBBBBBBBBBBBBBBBABBABBBROS7AECB7CA49E9F07D5D5 · Manmatter / at work

Never stop the first mistake of the obesity.
ℹ️ This is a MAAGMATTER project. Check our Page of landing If you are looking for Rust Consulting or training!
serde
is THE RUST LIBRARY FOR (DE) SERIALIZATION.
There is a catch, yet: serde
designed to abort aborts when an error occurred. Becomes an issue when dependent on serde
For the consignment of the given payment user – for example a request to a restful API.
There may be many errors in submitted payload, but serde_json
Report only the first it encountered before ceasing to dissipate. The API consumer then forced into a slow and failing feedback loop:
- Sending Request
- Receive a wrong return
- Fix the error
- Back to 1., until no more errors will be carried
That’s a bad developer experience. We have to make it better!
We should report multiple Often, in such a decrease in the number of API interactions needed to meet a well-formed payload.
That is the problem eserde
was born to resolve.
Let us consider this schema as an example of what we have mentioned:
#(derive(Debug, serde::Deserialize))
struct Package {
version: Version,
source: String,
}
#(derive(Debug, eserde::Deserialize))
struct Version {
major: u32,
minor: u32,
patch: u32,
}
Let’s try to grill an invalid JSON choose it by serde_json
:
let payload = r#"
{
"version": {
"major": 1,
"minor": "2"
},
"source": null
}"#;
let error = serde_json::from_str::<Package>(&payload).unwrap_err();
assert_eq!(
error.to_string(),
r#"invalid type: string "2", expected u32 at line 5 column 24"#
);
The first error is returned, as expected. But we know there’s more than that!
We lost in patch
field of Version
Structure and the source
The field cannot be null.
We’ll Move eserde
:
#(derive(Debug, eserde::Deserialize))
// ^^^^^^^^^^^^^^^^^^^
// Using `eserde::Deserialize`
// instead of `serde::Deserialize`!
struct Package {
version: Version,
source: String,
}
#(derive(Debug, eserde::Deserialize))
struct Version {
major: u32,
minor: u32,
patch: u32,
}
let payload = r#"
{
"version": {
"major": 1,
"minor": "2"
},
"source": null
}"#;
let errors = eserde::json::from_str::<Package>(&payload).unwrap_err();
// ^^^^^^^^^^^^
// We're not using `serde_json` directly here!
assert_eq!(
errors.to_string(),
r#"Something went wrong during deserialization:
- version.minor: invalid type: string "2", expected u32 at line 5 column 24
- version: missing field `patch`
- source: invalid type: null, expected a string at line 7 column 22
"#
);
Better, aren’t you?
We can now notify users to one to go that they need to adjust three different schema breaches.
Usage eserde
In your projects, add the following dependencies to your Cargo.toml
:
(dependencies)
eserde = { version = "0.1" }
serde = "1"
You must be:
- Replace all instances of
#(derive(serde::Deserialize))
OTHERS#(derive(eserde::Deserialize))
- Move to a
eserde
-Use function of enjoyment
eserde
provides first class support for JSON Weserialization, Gated behind json
Freight.
(dependencies)
# Activating the `json` feature
eserde = { version = "0.1", features = ("json") }
serde = "1"
If you work with JSON:
- replaced
serde_json::from_str
OTHERSeserde::json::from_str
- replaced
serde_json::from_slice
OTHERSeserde::json::from_slice
eserde::json
Consulting not supported from a reader, ie no equals
serde_json::from_reader
.
There is also a axum
together, eserde_axum
. It gives one eserde
-Pgaopered JSON Extractor as a drop-in replacement for axum
Established one.
The method used in eserde
The compatible, in principle, in all that exists serde
-baseed delerizers.
referring the source code of eserde::json::from_str
As a plan to follow for building a eserde
-Create deesialization function for another format.
eserde
designed to be the maximum highest serde
.
derive(eserde::Deserialize)
carry out both
serde::Deserialize
and eserde::EDeserialize
Honoring the behavior of all that serde
traits it supports.
If one of your fields do not apply eserde::EDeserialize
You can annot it
#(eserde(compat))
To drop back serde
Default Defe Defa default lockialization lookicization for that side of input.
#(derive(eserde::Deserialize))
struct Point {
// 👇 Use the `eserde::compat` attribute if you need to use
// a field type that doesn't implement `eserde::EDeserialize`
// and you can't derive `eserde::EDeserialize` for it (e.g. a third-party type)
#(eserde(compat))
x: Latitude,
// (...)
}
Check the documentation of eserde
get the macro for more details.
But how eserde
actually worked? Let’s keep using JSON as an example – equally applicable to other data formats.
We try to weserize input by serde_json
. If observing successes, we will return the brave amount to the caller.
// The source code for `eserde::json::from_str`.
pub fn from_str<'a, T>(s: &'a str) -> Result<T, DeserializationErrors>
where
T: EDeserialize<'a>,
{
let mut de = serde_json::Deserializer::from_str(s);
let error = match T::deserialize(&mut de) {
Ok(v) => {
return Ok(v);
}
Err(e) => e,
};
// (...)
}
There’s nothing new on a happy path – this is the same thing you do now with your own applications with vanilla serde
. We change the unhappy road.
Instead of returning to the caller error reported to serde_json
We make one pass through the input of use
eserde::EDeserialize::deserialize_for_errors
:
pub fn from_str<'a, T>(s: &'a str) -> Result<T, DeserializationErrors>
where
T: EDeserialize<'a>,
{
// (...) The code above (...)
let _guard = ErrorReporter::start_deserialization();
let mut de = serde_json::Deserializer::from_str(s);
let de = path::Deserializer::new(&mut de);
let errors = match T::deserialize_for_errors(de) {
Ok(_) => vec!(),
Err(_) => ErrorReporter::take_errors(),
};
let errors = if errors.is_empty() {
vec!(DeserializationError {
path: None,
details: error.to_string(),
})
} else {
errors
};
Err(DeserializationErrors::from(errors))
}
EDeserialize::deserialize_for_errors
formed the mistakes of disapproval of a thread-local buffer, began with ErrorReporter::start_deserialization
and taken later in ErrorReporter::take_errors
.
This underlying complexity is attached to eserde::json
Tasks, but useful to have a model of thinking what is happening under the hood when you plan to adopt eserde
.
eserde
A new library – may have issues and bugs not yet found. Try this before using it in production. If you have experienced any problems, please open an issue with our Gushup in the gutuph.
Except for defects, there are some depletions of eserde
Design Design:
- The input should be visited twice, so it cannot save from an unbearable reader.
- Input should be visited twice, so this happens slow than the one
serde::Deserialize
pass. #(derive(eserde::Deserialize))
creates additional code thanserde::Deserialize
(changed twice as much), so there’s a bigger effect on Vanillaserde
in your compilment times.
We believe the trade-off is useful for players to deal with the user, but you have to walk in your eyes open.
We plan to add first class support for additional data formats, in particular YAML and Toml. They are often used for configuration files, another scenario in which batch error can improve our developer experience.
We plan to increase support over more #(serde)
characteristics, thus shortened the friction to adopt eserde
in your cenerebase.
We plan to add first class support for validation, with a syntax similar to garde
and validator
. The key difference: Validation will be made as part of the process of de -ferialization. It is not necessary to remember to call .validate()
Then.
Copyright © 2025- Mainmatter GmbH (https://mainmatter.com), released under
with and Apache licenses.
https://opengraph.githubassets.com/97d62c907cf1dc90f617d385a248b2c2b09ed4e6307e99031c2ae87eee5cd3b7/mainmatter/eserde
2025-02-18 22:49:00