How can I apply a transformation to a field before serialization?
For example, how can I ensure that the fields lat
and lon
in this struct definition are rounded to at most 6 decimal places before being serialized?
#[derive(Debug, Serialize)]
struct NodeLocation {
#[serde(rename = "nodeId")]
id: u32,
lat: f32,
lon: f32,
}
Serde is a framework for serializing and deserializing Rust data structures efficiently and generically. The Serde ecosystem consists of data structures that know how to serialize and deserialize themselves along with data formats that know how to serialize and deserialize other things.
Serde. Serde, the incumbent serialization/deserialization library, is elegant, flexible, fast to run, and slow to compile.
The Hive JSON SerDe is commonly used to process JSON data like events. These events are represented as single-line strings of JSON-encoded text separated by a new line. The Hive JSON SerDe does not allow duplicate keys in map or struct key names.
serialize_with
attributeYou can use the serialize_with
attribute to provide a custom serialization function for your field:
use serde::{Serialize, Serializer}; // 1.0.104
fn round_serialize<S>(x: &f32, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_f32(x.round())
}
#[derive(Debug, Serialize)]
pub struct NodeLocation {
#[serde(rename = "nodeId")]
id: u32,
#[serde(serialize_with = "round_serialize")]
lat: f32,
#[serde(serialize_with = "round_serialize")]
lon: f32,
}
(I've rounded to the nearest integer to avoid the topic "what is best way to round a float to k decimal places").
serde::Serialize
The other semi-manual approach is to create a separate struct with auto-derived serialization, and implement your serialization using that:
use serde::{Serialize, Serializer}; // 1.0.104
#[derive(Debug)]
pub struct NodeLocation {
id: u32,
lat: f32,
lon: f32,
}
impl serde::Serialize for NodeLocation {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Implement your preprocessing in `from`.
RoundedNodeLocation::from(self).serialize(s)
}
}
#[derive(Debug, Serialize)]
pub struct RoundedNodeLocation {
#[serde(rename = "nodeId")]
id: u32,
lat: f32,
lon: f32,
}
impl<'a> From<&'a NodeLocation> for RoundedNodeLocation {
fn from(other: &'a NodeLocation) -> Self {
Self {
id: other.id,
lat: other.lat.round(),
lon: other.lon.round(),
}
}
}
Notably, this allows you to also add or remove fields as the "inner" serialized type can do basically whatever it wants.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With