Use of serde for Datastore encoding/decoding
plippe opened this issue · 1 comments
plippe commented
Hi,
Serde is a create library to encode and decode values. The list of supported data formats is quite large and contains json.
Are there any reasons Serde wasn't used for Google's Datastore?
plippe commented
I have written some helpers for my own application. Beware, my solution doesn't handle all cases. I have mapped some types to Null
instead of properly handling the issue.
fn recursive_into_value_with_serialize(value: serde_json::value::Value) -> Value {
match value {
serde_json::value::Value::Null => Value::EntityValue(HashMap::new()),
serde_json::value::Value::Bool(b) => Value::BooleanValue(b),
serde_json::value::Value::String(s) => Value::StringValue(s),
serde_json::value::Value::Number(n) => n
.as_i64()
.map(Value::IntegerValue)
.or_else(|| n.as_f64().map(Value::DoubleValue))
.unwrap(),
serde_json::value::Value::Array(vec) => {
let vec = vec
.into_iter()
.map(recursive_into_value_with_serialize)
.collect::<Vec<Value>>();
Value::ArrayValue(vec)
}
serde_json::value::Value::Object(map) => {
let map = map
.into_iter()
.map(|(k, v)| (k, recursive_into_value_with_serialize(v)))
.collect::<HashMap<String, Value>>();
Value::EntityValue(map)
}
}
}
pub fn into_value_with_serialize<A>(value: A) -> Value
where
A: serde::Serialize,
{
let value = serde_json::to_value(value).unwrap();
recursive_into_value_with_serialize(value)
}
fn recursive_from_value_with_deserialize(value: Value) -> serde_json::value::Value {
match value {
Value::BooleanValue(b) => serde_json::value::Value::Bool(b),
Value::IntegerValue(i) => serde_json::value::Value::Number(i.into()),
Value::DoubleValue(f) => {
serde_json::value::Value::Number(serde_json::Number::from_f64(f).unwrap())
}
Value::StringValue(s) => serde_json::value::Value::String(s),
Value::EntityValue(map) if map.is_empty() => serde_json::value::Value::Null,
Value::EntityValue(map) => {
let map = map
.into_iter()
.map(|(k, v)| (k, recursive_from_value_with_deserialize(v)))
.collect::<serde_json::map::Map<String, serde_json::value::Value>>();
serde_json::value::Value::Object(map)
}
Value::ArrayValue(vec) => {
let vec = vec
.into_iter()
.map(recursive_from_value_with_deserialize)
.collect::<Vec<serde_json::value::Value>>();
serde_json::value::Value::Array(vec)
}
Value::KeyValue(_) => serde_json::value::Value::Null,
Value::TimestampValue(_) => serde_json::value::Value::Null,
Value::BlobValue(_) => serde_json::value::Value::Null,
Value::GeoPointValue(_, _) => serde_json::value::Value::Null,
}
}
pub fn from_value_with_deserialize<A>(
value: google_cloud::datastore::Value,
) -> std::result::Result<A, ConvertError>
where
A: for<'de> serde::Deserialize<'de>,
{
let value = recursive_from_value_with_deserialize(value);
serde_json::from_value::<A>(value).map_err(|_| ConvertError::MissingProperty("".to_owned()))
}
Lastly, those helpers are used in the IntoValue
and FromValue
implementations.
impl FromValue for MyType {
fn from_value(value: Value) -> std::result::Result<Self, ConvertError> {
datastore::from_value_with_deserialize::<Self>(value)
}
}
impl IntoValue for MyType {
fn into_value(self) -> Value {
datastore::into_value_with_serialize::<Self>(self)
}
}
Hope this helps others and possibly makes it into the next version of the crate.