I have a struct which contains a weak ref to a pre-existing Arc. Is there a way for me to pass this Arc to the struct's deserializer, without writing my own deserializer/visitor?
Example struct / deserialize:
struct SomeGlobal;
#[derive(Deserialize)]
struct SomeState {
pub a: i32,
pub b: Weak<SomeGlobal>,
};
fn magic_deserialize(value: serde_json::Value, g: Arc<SomeGlobal>) -> Result<SomeState, ...> {
// what goes here?
}
#[test]
fn example() {
let g = Arc::new(SomeGlobal);
let value = json!({
"a": 5
});
let state = magic_deserialize(value, g.clone()).unwrap();
// state.a == 5
// state.b == weak ref to g
}
Since Weak<T> implements Default, you can simply annotate b with #[serde(skip)] to have serde skip serializing this field, and set it to default when deserializing.
#[derive(Debug, Serialize, Deserialize)]
struct SomeState {
pub a: i32,
#[serde(skip)]
pub b: Weak<SomeGlobal>,
}
In my rust project, I'm trying to make a db like implementation that can store the state of a user from different integrations that all implement same functions through a trait, and can be written to a file later using serde with this code:
#[derive(Serialize, Deserialize, Debug)]
pub struct State {
pub current: HashMap<Bytes, Box<dyn UserState>>
pub changes: HashMap<u64, Vec<UserStateChange>>
}
pub trait UserState {
fn id(&self) -> Bytes;
fn apply_change(&mut self, change: UserStateChange);
}
#[derive(Serialize, Deserialize, Debug)]
pub enum UserStateChange {
Foo(FooChange),
Bar(BarChange)
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FooState {
id: Bytes,
count: u64,
start: u64
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FooChange {
count_delta: i64
}
impl UserState for FooState {
fn id(&self) -> Bytes {self.id.clone()}
fb apply_change(&mut self, change: UserStateChange) {
let UserStateChange::Foo(change) = change;
todo!();
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BarState {
id: Bytes,
attempts: u64
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BarChange {
attempt_delta: i64
}
impl UserState for BarState {
fn id(&self) -> Bytes {self.id.clone()}
fb apply_change(&mut self, change: UserStateChange) {
let UserStateChange::Bar(change) = change;
// apply logic
}
}
How can I make it so that it can store and serialize the dynamic trait, while ensuring that it supports the apply_change function, and can use its own structure for storing changes with relevant fields?
I am using rust rocket as the http server side. On the server side, I define a rust struct like this to receive the data from client side:
#[derive(Deserialize, Serialize)]
#[allow(non_snake_case)]
pub struct PlayRecordRequest {
pub id: i64,
pub title: String,
pub url: String,
pub mvId: i64,
pub album: MusicAlbum,
pub artist: Vec<Artist>,
}
and recieve in the http controller like this:
#[post("/user/v1/save-play-record", data = "<record>")]
pub fn save_play_record<'r>(
record: Json<PlayRecordRequest>,
) -> content::Json<String> {
info!("save play record,title:{}",record.title);
save_play_record_impl(record);
let res = ApiResponse {
result: "ok".to_string(),
..Default::default()
};
let result_json = serde_json::to_string(&res).unwrap();
return content::Json(result_json);
}
the problem is that when the client side did not have some fields, the code run into error. is it possible to auto fit the field for Deserialize, if the client have the field, Deserialize it normally, if the client did not contains some fields, just ignore it and do not run into error. I read the official document of serde and find the skip annotation. But the annotation just use to mark the field should be ignored, what I want is that the struct could auto fit all field exists or not. is it possible to do like this?
There are two ways to handle this.
First is with options. Options imply that the data may or may not exist. If the data is null or missing it convert to an Option::none value. You can preserve the lack of data on serialization if you add #[serde(skip_serializing_if = "Option::is_none")]
Second option is to apply defaults to the value if the data is missing. Although from your use case this doesn't seem to be ideal.
Here is a code snippet of the two cases that you can run on https://play.rust-lang.org/:
use::serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug)]
#[allow(non_snake_case)]
pub struct Foo {
#[serde(skip_serializing_if = "Option::is_none")]
pub bar: Option<i64>,
#[serde(default = "default_baz")]
pub baz: i64,
}
fn default_baz()-> i64{
3
}
fn main(){
let data = r#"{"bar": null, "baz": 1}"#;
let v: Foo = serde_json::from_str(data).unwrap();
println!("{:#?}", v);
let s = serde_json::to_string(&v).unwrap();
println!("Don't serialize None values: {:#?}", s);
let data = r#"{"missing_bar": null}"#;
let v: Foo = serde_json::from_str(data).unwrap();
println!("{:#?}", v)
}
I have struct from which I want to extract the data.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunResult {
pub runs: Vec<RunDetails>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunDetails {
pub details: String,
pub name: String,
pub id: String,
pub type: String,
}
I am getting the data which got inserted in above struct in the variable result which I am iterating below
let my_result:RunResult = result
.runs
.into_iter()
.filter(|line| line.details.contains(&"foo"))
.collect();
I am getting the error as
value of type `RunResult` cannot be built from `std::iter::Iterator<Item=RunDetails>`
I want to get the record where type=OK, details contains foo with highest id.
How can I achieve this?
Check out the documentation for collect here.
Your struct RunResult should implement FromIterator<RunDetails>.
Try adding this (untested by me):
impl FromIterator<RunDetails> for RunResult {
fn from_iter<I: IntoIterator<Item=RunDetails>>(iter: I) -> Self {
Self {
runs: Vec::from_iter(iter);
}
}
}
This example code:
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
struct Foo {
bar: String,
baz: Baz
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
enum Baz {
Quux(u32),
Flob,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
struct Bish {
bash: u16,
bosh: i8
}
fn main() -> std::io::Result<()> {
let mut btree: BTreeMap<Foo, Bish> = BTreeMap::new();
let foo = Foo {
bar: "thud".to_string(),
baz: Baz::Flob
};
let bish = Bish {
bash: 1,
bosh: 2
};
println!("foo: {}", serde_json::to_string(&foo)?);
println!("bish: {}", serde_json::to_string(&bish)?);
btree.insert(foo, bish);
println!("btree: {}", serde_json::to_string(&btree)?);
Ok(())
}
gives the runtime output/error:
foo: {"bar":"thud","baz":"Flob"}
bish: {"bash":1,"bosh":2}
Error: Custom { kind: InvalidData, error: Error("key must be a string", line: 0, column: 0) }
I've googled this, and found that the problem is that the serialiser would be trying to write:
{{"bar":"thud","baz":"Flob"}:{"bash":1,"bosh":2}}}
which is not valid JSON, as keys must be strings.
The internet tells me to write custom serialisers.
This is not a practical option, as I have a large number of different non-string keys.
How can I make serde_json serialise to (and deserialise from):
{"{\"bar\":\"thud\",\"baz\":\"Flob\"}":{"bash":1,"bosh":2}}
for arbitrary non-string keys in BTreeMap and HashMap?
Although OP decided not to use JSON in the end, I have written a crate that does exactly what the original question asked for: https://crates.io/crates/serde_json_any_key. Using it is as simple as a single function call.
Because this is StackOverflow and just a link is not a sufficient answer, here is a complete implementation, combining code from v1.1 of the crate with OP's main function, replacing only the final call to serde_json::to_string:
extern crate serde;
extern crate serde_json;
use serde::{Serialize, Deserialize};
use std::collections::BTreeMap;
mod serde_json_any_key {
use std::any::{Any, TypeId};
use serde::ser::{Serialize, Serializer, SerializeMap, Error};
use std::cell::RefCell;
struct SerializeMapIterWrapper<'a, K, V>
{
pub iter: RefCell<&'a mut (dyn Iterator<Item=(&'a K, &'a V)> + 'a)>
}
impl<'a, K, V> Serialize for SerializeMapIterWrapper<'a, K, V> where
K: Serialize + Any,
V: Serialize
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: Serializer
{
let mut ser_map = serializer.serialize_map(None)?;
let mut iter = self.iter.borrow_mut();
// handle strings specially so they don't get escaped and wrapped inside another string
if TypeId::of::<K>() == TypeId::of::<String>() {
while let Some((k, v)) = iter.next() {
let s = (k as &dyn Any).downcast_ref::<String>().ok_or(S::Error::custom("Failed to serialize String as string"))?;
ser_map.serialize_entry(s, &v)?;
}
} else {
while let Some((k, v)) = iter.next() {
ser_map.serialize_entry(match &serde_json::to_string(&k)
{
Ok(key_string) => key_string,
Err(e) => { return Err(e).map_err(S::Error::custom); }
}, &v)?;
}
}
ser_map.end()
}
}
pub fn map_iter_to_json<'a, K, V>(iter: &'a mut dyn Iterator<Item=(&'a K, &'a V)>) -> Result<String, serde_json::Error> where
K: Serialize + Any,
V: Serialize
{
serde_json::to_string(&SerializeMapIterWrapper {
iter: RefCell::new(iter)
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
struct Foo {
bar: String,
baz: Baz
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
enum Baz {
Quux(u32),
Flob,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
struct Bish {
bash: u16,
bosh: i8
}
fn main() -> std::io::Result<()> {
let mut btree: BTreeMap<Foo, Bish> = BTreeMap::new();
let foo = Foo {
bar: "thud".to_string(),
baz: Baz::Flob
};
let bish = Bish {
bash: 1,
bosh: 2
};
println!("foo: {}", serde_json::to_string(&foo)?);
println!("bish: {}", serde_json::to_string(&bish)?);
btree.insert(foo, bish);
println!("btree: {}", serde_json_any_key::map_iter_to_json(&mut btree.iter())?);
Ok(())
}
Output:
foo: {"bar":"thud","baz":"Flob"}
bish: {"bash":1,"bosh":2}
btree: {"{\"bar\":\"thud\",\"baz\":\"Flob\"}":{"bash":1,"bosh":2}}
After discovering Rusty Object Notation, I realised that I was pushing a RON-shaped peg into a JSON-shaped hole.
The correct solution was to use JSON for the interface with the outside world, and RON for human-readable local data storage.