I have the following struct
#[derive(Serialize)]
pub struct MyStruct {
pub id: String,
pub score: f32,
pub json: String,
}
The json
field always contains a valid JSON object already stringified.
Given an instance, I would like to serialize it with the JSON content. Something like:
let a = MyStruct {
id: "my-id".to_owned(),
score: 20.3,
json: r#"{
"ffo": 4
}"#,
};
let r = to_string(&a).unwrap();
assert_eq!(r, r#"{
"id": "my-id",
"score": 20.3,
"json": {
"ffo": 4
}
}"#);
NB: I don't need to support different serialization formats, only JSON.
NB2: I'm sure that json
field always contains a valid JSON object.
NB3: commonly I use serde but I'm open to using different libraries.
How can I do that?
Edit: I would like to avoid deserializing the string during the serialization if possible.
serde_json
has a raw_value
feature for something like this:
Cargo.toml
# ...
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["raw_value"] }
lib.rs
use serde::{Serializer, Serialize};
use serde_json::{self, value::RawValue};
#[derive(Serialize)]
pub struct MyStruct {
pub id: String,
pub score: f32,
#[serde(serialize_with = "serialize_raw_json")]
pub json: String,
}
fn serialize_raw_json<S>(json: &str, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// This should be pretty efficient: it just checks that the string is valid;
// it doesn't parse it into a new data structure.
let v: &RawValue = serde_json::from_str(json).expect("invalid json");
v.serialize(s)
}
#[test]
fn test_serialize() {
let a = MyStruct {
id: "my-id".to_owned(),
score: 20.3,
json: r#"{
"ffo": 4
}"#
.to_string(),
};
let r = serde_json::to_string(&a).unwrap();
assert_eq!(
r,
r#"{"id":"my-id","score":20.3,"json":{
"ffo": 4
}}"#
);
}
But the simplest (and most error-prone and least extensible) solution is simple string manipulation:
#[derive(Serialize)]
pub struct MyStruct {
pub id: String,
pub score: f32,
// IMPORTANT: don't serialize this field at all
#[serde(skip)]
pub json: String,
}
fn serialize(a: &MyStruct) -> String {
let mut r = serde_json::to_string(&a).unwrap();
// get rid of trailing '}'
r.pop();
// push the key
r.push_str(r#","json":"#);
// push the value
r.push_str(&a.json);
// push the closing brace
r.push('}');
r
}
#[test]
fn test_serialize() {
let a = MyStruct {
id: "my-id".to_owned(),
score: 20.3,
json: r#"{
"ffo": 4
}"#
.to_string(),
};
let r = serialize(&a);
assert_eq!(
r,
r#"{"id":"my-id","score":20.3,"json":{
"ffo": 4
}}"#
);
}