Deserialize JSON object (as text) inside JSON bject - json

I'm looking for a better solution for deserializing nested JSON object like this
{"name":"John Doe","age":43,"address":"{\"street\":\"10 Downing Street\",\"city\":\"London\"}"}
Using this code I do the job
use serde_derive::{Deserialize, Serialize};use serde_json::{Result, Value};
#[derive(Serialize, Deserialize)]
struct Person{
name: String,
address: Value
}
#[derive(Debug, Serialize, Deserialize)]
struct Address{
street: String,
city: String
}
impl From<Person> for Address{
fn from(p: Person) -> Self {
let str_val: String = serde_json::from_value(p.address).unwrap();
let ad: Self = serde_json::from_str(&str_val).unwrap();
ad
}
}
fn main() -> Result<()> {
let data = r#"{"name":"John Doe","age":43,"address":"{\"street\":\"10 Downing Street\",\"city\":\"London\"}"}"#;
let p: Person = serde_json::from_str(data).unwrap();
println!("{:?}", Address::from(p));
Ok(())
}
But It seems to me that may be a better way to do it. Any suggestion?

But It seems to me that may be a better way to do it. Any suggestion?
Well address: Value does not seem useful, because he Value in this case is a String: eprintln!("{:?}", p.address); will print
String("{\"street\":\"10 Downing Street\",\"city\":\"London\"}")
So you could just have address: String, and then deserialise from that directly:
serde_json::from_str(&p.address).unwrap()
Alternatively you can use deserialize_with or create the deserializer for Person by hand, such that you can recursively invoke serde_json in order to deserialize Address while deserializing Person.
Probably the biggest advantage is you should not have to allocate a string for Address, you can deserialize from the borrowed data.

Adding to #Masklinn's answer, here is a working version based on deserialize_with:
use serde::{Deserialize, Deserializer};
#[derive(Debug, Deserialize)]
struct Address {
street: String,
city: String,
}
#[derive(Debug, Deserialize)]
struct Person {
name: String,
#[serde(deserialize_with = "deserialize_nested_address")]
address: Address,
}
fn deserialize_nested_address<'de, D>(data: D) -> Result<Address, D::Error>
where
D: Deserializer<'de>,
{
struct AddressVisitor;
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
type Value = Address;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string containing a json encoded address")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Address, E> {
serde_json::from_str(v).map_err(E::custom)
}
}
data.deserialize_any(AddressVisitor)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = r#"{"name":"John Doe","age":43,"address":"{\"street\":\"10 Downing Street\",\"city\":\"London\"}"}"#;
let p: Person = serde_json::from_str(data)?;
println!("{:#?}", p);
Ok(())
}
Person {
name: "John Doe",
address: Address {
street: "10 Downing Street",
city: "London",
},
}
You can of course do it both ways:
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Serialize, Deserialize)]
struct Address {
street: String,
city: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct Person {
name: String,
#[serde(
serialize_with = "serialize_nested_address",
deserialize_with = "deserialize_nested_address"
)]
address: Address,
}
fn deserialize_nested_address<'de, D>(data: D) -> Result<Address, D::Error>
where
D: Deserializer<'de>,
{
struct AddressVisitor;
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
type Value = Address;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string containing a json encoded address")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Address, E> {
serde_json::from_str(v).map_err(E::custom)
}
}
data.deserialize_any(AddressVisitor)
}
fn serialize_nested_address<S>(address: &Address, data: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
use serde::ser::Error;
let s = serde_json::to_string(address).map_err(S::Error::custom)?;
data.serialize_str(&s)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data = r#"{"name":"John Doe","age":43,"address":"{\"street\":\"10 Downing Street\",\"city\":\"London\"}"}"#;
let p: Person = serde_json::from_str(data)?;
println!("{:#?}", p);
let p_ser = serde_json::to_string(&p)?;
println!("Serialized: {}", p_ser);
Ok(())
}
Person {
name: "John Doe",
address: Address {
street: "10 Downing Street",
city: "London",
},
}
Serialized: {"name":"John Doe","address":"{\"street\":\"10 Downing Street\",\"city\":\"London\"}"}

Related

Error in Custom Deserialization of String Vec from JSON with Serde in Rust

I am trying to deserialize:
"comments": ["string"]
//And
"comments": "string"
To :
#[derive(Debug)]
#[derive(serde::Deserialize)]
pub struct Block {
pub comments: Comments,
}
With :
pub fn deserialize_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::Deserialize<'de>,
{
let s:T = serde::Deserialize::deserialize(deserializer)?; // how to peek_char
Ok(vec![s])
}
Playground
Note that this is the minimal version, I need these attributes for many other types.
As Stargateur suggested in the comment,
I came up with something like this...
pub fn deserialize_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::Deserialize<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum VecOrType<T> {
Vec(Vec<T>),
Type(T),
}
let value:VecOrType<T> = serde::Deserialize::deserialize(deserializer)?;
match value {
VecOrType::Vec(vec) => {
Ok(vec)
},
VecOrType::Type(typ) => {
Ok(vec!(typ))
}
}
}
playground
Thanks Stargateur and Ahmed Masud

Serialize/Deserialize CSV with nested enum/struct with serde in Rust

I want to serialize/deserialize a CSV file with variable row length and content, like the following:
./test.csv
Message,20200202T102030,Some message content
Measurement,20200202T102031,10,30,40,2
AnotherMeasurement,20200202T102034,0,2
In my opinion, the easiest way to represent this is the following enum:
#[derive(Debug, Serialize, Deserialize)]
pub enum Record {
Message { timestamp: String, content: String }, // timestamp is String because of simplicity
Measurement { timestamp: String, a: u32, b: u32, c: u32, d: u32 },
AnotherMeasurement { timestamp: String, a: u32, b: u32 },
}
Cargo.toml
[dependencies]¬
csv = "^1.1.6"¬
serde = { version = "^1", features = ["derive"] }
Running the following
main.rs
fn example() -> Result<(), Box<dyn Error>> {
let mut rdr = csv::ReaderBuilder::new()
.has_headers(false)
.delimiter(b',')
.flexible(true)
.double_quote(false)
.from_path("./test.csv")
.unwrap();
for result in rdr.deserialize() {
let record: Record = result?;
println!("{:?}", record);
}
Ok(())
}
fn write_msg() -> Result<(), Box<dyn Error>> {
let msg = Record::Message {
timestamp: String::from("time"),
content: String::from("content"),
};
let mut wtr = csv::WriterBuilder::new()
.has_headers(false)
.flexible(true)
.double_quote(false)
.from_writer(std::io::stdout());
wtr.serialize(msg)?;
wtr.flush()?;
Ok(())
}
fn main() {
if let Err(err) = example() {
println!("error running example: {}", err);
}
if let Err(err) = write_msg() {
println!("error running example: {}", err);
}
}
prints
error running example: CSV deserialize error: record 0 (line: 1, byte: 0): invalid type: unit variant, expected struct variant
error running example: CSV write error: serializing enum struct variants is not supported
Is there an easy solution to do this with serde and csv? I feel like I missed one or two serde attributes, but I was not able to find the right one in the documentation yet.
EDITS
Netwave suggested adding the #[serde(tag = "type")] attribute. Serializing now works, Deserializing gives the following error:
error running example: CSV deserialize error: record 0 (line: 1, byte: 0): invalid type: string "Message", expected internally tagged enum Record
Research I did that did not lead to a solution yet
Is there a way to "flatten" enums for (de)serialization in Rust?
https://docs.rs/csv/1.1.6/csv/tutorial/index.html
Custom serde serialization for enum type
https://serde.rs/enum-representations.html
Make your enum tagged (internally tagged specifically):
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Record {
Message { timestamp: String, content: String }, // timestamp is String because of simplicity
Measurement { timestamp: String, a: u32, b: u32, c: u32, d: u32 },
AnotherMeasurement { timestamp: String, a: u32, b: u32 },
}
Playground

how can I fix sql type error with diesel and juniper for mysql in graphQL mutation?

I confronted the error logs below when I try to create mutation with graphGL and mysql via diesel.
currently the type of enum is just diesel's type but I want to make that with graphQl's type.
I implemented Customer Structure for graphQL like below.
is it not enough ? and do you have any idea to fix that ?
Thanks
Error log
error[E0277]: the trait bound `graphql::Customer: diesel::Queryable<(diesel::sql_types::Unsigned<diesel::sql_types::BigInt>, diesel::sql_types::Text, diesel::sql_types::Text, diesel::sql_types::Timestamp, diesel::sql_types::Timestamp), _>` is not satisfied
--> src/graphql.rs:60:14
|
60 | .first::<crate::graphql::Customer>(&executor.context().db_con)
| ^^^^^ the trait `diesel::Queryable<(diesel::sql_types::Unsigned<diesel::sql_types::BigInt>, diesel::sql_types::Text, diesel::sql_types::Text, diesel::sql_types::Timestamp, diesel::sql_types::Timestamp), _>` is not implemented for `graphql::Customer`
|
= note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<_, graphql::Customer>` for `diesel::query_builder::SelectStatement<schema::customers::table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, diesel::query_builder::limit_clause::LimitClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>>`
src/graphql.rs
use std::convert::From;
use std::sync::Arc;
use chrono::NaiveDateTime;
use actix_web::{web, Error, HttpResponse};
use futures01::future::Future;
use juniper::http::playground::playground_source;
use juniper::{http::GraphQLRequest, Executor, FieldResult, FieldError,ID};
use juniper_from_schema::graphql_schema_from_file;
use diesel::prelude::*;
use itertools::Itertools;
use crate::schema::customers;
use crate::{DbCon, DbPool};
graphql_schema_from_file!("src/schema.graphql");
pub struct Context {
db_con: DbCon,
}
impl juniper::Context for Context {}
pub struct Query;
pub struct Mutation;
impl QueryFields for Query {
fn field_customers(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Customer, Walked>,
) -> FieldResult<Vec<Customer>> {
//type FieldResult<T> = Result<T, String>;
customers::table
.load::<crate::models::Customer>(&executor.context().db_con)
.and_then(|customers| Ok(customers.into_iter().map_into().collect()))
.map_err(Into::into)
}
}
impl MutationFields for Mutation {
fn field_create_customer(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Customer, Walked>,
name: String,
email: String,
) -> FieldResult<Customer> {
//type FieldResult<T> = Result<T, String>;
let new_customer = crate::models::NewCustomer { name: name, email: email};
diesel::insert_into(customers::table)
.values(&new_customer)
.execute(&executor.context().db_con);
customers::table
.first::<crate::graphql::Customer>(&executor.context().db_con)
.map_err(Into::into)
}
}
pub struct Customer {
id: u64,
name: String,
email: String,
created_at: NaiveDateTime,
updated_at: NaiveDateTime,
}
impl CustomerFields for Customer {
fn field_id(&self, _: &Executor<'_, Context>) -> FieldResult<juniper::ID> {
Ok(juniper::ID::new(self.id.to_string()))
}
fn field_name(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
Ok(&self.name)
}
fn field_email(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
Ok(&self.email)
}
}
impl From<crate::models::Customer> for Customer {
fn from(customer: crate::models::Customer) -> Self {
Self {
id: customer.id,
name: customer.name,
email: customer.email,
created_at: customer.created_at,
updated_at: customer.updated_at,
}
}
}
fn playground() -> HttpResponse {
let html = playground_source("");
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html)
}
fn graphql(
schema: web::Data<Arc<Schema>>,
data: web::Json<GraphQLRequest>,
db_pool: web::Data<DbPool>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let ctx = Context {
db_con: db_pool.get().unwrap(),
};
web::block(move || {
let res = data.execute(&schema, &ctx);
Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?)
})
.map_err(Error::from)
.and_then(|customer| {
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(customer))
})
}
pub fn register(config: &mut web::ServiceConfig) {
let schema = std::sync::Arc::new(Schema::new(Query, Mutation));
config
.data(schema)
.route("/", web::post().to_async(graphql))
.route("/", web::get().to(playground));
}
src/models.rs
#[derive(Queryable, Identifiable, AsChangeset, Clone, PartialEq, Debug)]
pub struct Customer {
pub id: u64,
pub name: String,
pub email: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
}
use super::schema::customers;
#[derive(Queryable,Insertable, AsChangeset)]
#[table_name="customers"]
pub struct NewCustomer {
pub name: String,
pub email: String,
}
Dependencies
[dependencies]
diesel = { version = "1.4.5", features = ["mysql", "r2d2", "chrono"] }
dotenv = "~0.15"
serde = "~1.0"
serde_derive = "~1.0"
serde_json = "~1.0"
chrono = "~0.4"
rand = "0.7.3"
actix-web = "1.0.9"
actix-cors = "0.1.0"
juniper = "0.14.1"
juniper-from-schema = "0.5.1"
juniper-eager-loading = "0.5.0"
r2d2_mysql = "*"
r2d2-diesel = "0.16.0"
mysql = "*"
r2d2 = "*"
futures01 = "0.1.29"
itertools = "0.8.2"
src/schema.graphql
schema {
query: Query
mutation: Mutation
}
type Query {
customers: [Customer!]! #juniper(ownership: "owned")
}
type Mutation {
createCustomer(
name: String!,
email: String!,
): Customer! #juniper(ownership: "owned")
}
type Customer {
id: ID! #juniper(ownership: "owned")
name: String!
email: String!
}
src/schema.rs
table! {
customers (id) {
id -> Unsigned<Bigint>,
name -> Varchar,
email -> Varchar,
created_at -> Timestamp,
updated_at -> Timestamp,
}
}
As the error message mentions you need to implement Queryable for your struct Customer in src/graphql.rs (That struct in just below where the error message points to). The easiest way to do that is to just add a #[derive(Queryable)] to this struct.

How do I deserialize a struct that has a generic type that implements a trait? [duplicate]

I want to use Serde to create an array with error messages as well as proper objects:
extern crate serde; // 1.0.70
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde_json; // 1.0.24
#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
error: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
age: i32,
name: String,
}
fn get_results(ages: Vec<i32>) -> Vec<MyAge> {
let mut results = vec![];
for age in ages {
if age < 100 && age > 0 {
results.push(MyAge {
age: age,
name: String::from("The dude"),
});
} else {
results.push(MyError {
error: String::from(format!("{} is invalid age", age)),
});
}
}
results
}
When I pass in the Vec [1, -6, 7] I want to serialize to the JSON:
[{"age": 1, "name": "The dude"},{"error": "-6 is invalid age"},{"age": 7, "name": "The dude"}]
How do I do that? Knowing how to deserialize such an array would be nice as well.
Here's one way of doing that:
#[macro_use]
extern crate serde_derive; // 1.0.117
extern crate serde; // 1.0.117
extern crate serde_json; // 1.0.59
#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
error: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
age: i32,
name: String,
}
#[derive(Debug)]
enum AgeOrError {
Age(MyAge),
Error(MyError),
}
impl serde::Serialize for AgeOrError {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
&AgeOrError::Age(ref my_age) => serializer.serialize_some(my_age),
&AgeOrError::Error(ref my_error) => serializer.serialize_some(my_error),
}
}
}
enum AgeOrErrorField {
Age,
Name,
Error,
}
impl<'de> serde::Deserialize<'de> for AgeOrErrorField {
fn deserialize<D>(deserializer: D) -> Result<AgeOrErrorField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct AgeOrErrorFieldVisitor;
impl<'de> serde::de::Visitor<'de> for AgeOrErrorFieldVisitor {
type Value = AgeOrErrorField;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "age or error")
}
fn visit_str<E>(self, value: &str) -> Result<AgeOrErrorField, E>
where
E: serde::de::Error,
{
Ok(match value {
"age" => AgeOrErrorField::Age,
"name" => AgeOrErrorField::Name,
"error" => AgeOrErrorField::Error,
_ => panic!("Unexpected field name: {}", value),
})
}
}
deserializer.deserialize_any(AgeOrErrorFieldVisitor)
}
}
impl<'de> serde::Deserialize<'de> for AgeOrError {
fn deserialize<D>(deserializer: D) -> Result<AgeOrError, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_map(AgeOrErrorVisitor)
}
}
struct AgeOrErrorVisitor;
impl<'de> serde::de::Visitor<'de> for AgeOrErrorVisitor {
type Value = AgeOrError;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "age or error")
}
fn visit_map<A>(self, mut map: A) -> Result<AgeOrError, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut age: Option<i32> = None;
let mut name: Option<String> = None;
let mut error: Option<String> = None;
loop {
match map.next_key()? {
Some(AgeOrErrorField::Age) => age = map.next_value()?,
Some(AgeOrErrorField::Name) => name = map.next_value()?,
Some(AgeOrErrorField::Error) => error = map.next_value()?,
None => break,
}
}
if let Some(error) = error {
Ok(AgeOrError::Error(MyError { error: error }))
} else {
Ok(AgeOrError::Age(MyAge {
age: age.expect("!age"),
name: name.expect("!name"),
}))
}
}
}
fn get_results(ages: &[i32]) -> Vec<AgeOrError> {
let mut results = Vec::with_capacity(ages.len());
for &age in ages.iter() {
if age < 100 && age > 0 {
results.push(AgeOrError::Age(MyAge {
age: age,
name: String::from("The dude"),
}));
} else {
results.push(AgeOrError::Error(MyError {
error: format!("{} is invalid age", age),
}));
}
}
results
}
fn main() {
let v = get_results(&[1, -6, 7]);
let serialized = serde_json::to_string(&v).expect("Can't serialize");
println!("serialized: {}", serialized);
let deserialized: Vec<AgeOrError> =
serde_json::from_str(&serialized).expect("Can't deserialize");
println!("deserialized: {:?}", deserialized);
}
Note that in deserialization we can't reuse the automatically generated deserializers because:
deserialization is kind of streaming the fields to us, we can't peek into the stringified JSON representation and guess what it is;
we don't have access to the serde::de::Visitor implementations that Serde generates.
Also I did a shortcut and panicked on errors. In production code you'd want to return the proper Serde errors instead.
Another solution would be to make a merged structure with all fields optional, like this:
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde; // 1.0.70
extern crate serde_json; // 1.0.24
#[derive(Debug)]
pub struct MyError {
error: String,
}
#[derive(Debug)]
pub struct MyAge {
age: i32,
name: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAgeOrError {
#[serde(skip_serializing_if = "Option::is_none")]
age: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
impl MyAgeOrError {
fn from_age(age: MyAge) -> MyAgeOrError {
MyAgeOrError {
age: Some(age.age),
name: Some(age.name),
error: None,
}
}
fn from_error(error: MyError) -> MyAgeOrError {
MyAgeOrError {
age: None,
name: None,
error: Some(error.error),
}
}
}
fn get_results(ages: &[i32]) -> Vec<MyAgeOrError> {
let mut results = Vec::with_capacity(ages.len());
for &age in ages.iter() {
if age < 100 && age > 0 {
results.push(MyAgeOrError::from_age(MyAge {
age: age,
name: String::from("The dude"),
}));
} else {
results.push(MyAgeOrError::from_error(MyError {
error: format!("{} is invalid age", age),
}));
}
}
results
}
fn main() {
let v = get_results(&[1, -6, 7]);
let serialized = serde_json::to_string(&v).expect("Can't serialize");
println!("serialized: {}", serialized);
let deserialized: Vec<MyAgeOrError> =
serde_json::from_str(&serialized).expect("Can't deserialize");
println!("deserialized: {:?}", deserialized);
}
I'd vouch for this one because it allows the Rust structure (e.g. MyAgeOrError) to match the layout of your JSON. That way the JSON layout becomes documented in the Rust code.
P.S. Lately I tend to delay the decoding of optional or dynamically typed JSON parts with the help of RawValue. It's tricky to serialize them though, because RawValue is a borrow. For instance, and to help with serialization, one can intern a RawValue, promoting it to the 'static lifetime:
use serde_json::value::{RawValue as RawJson};
fn intern_raw_json(raw_json: Box<RawJson>) -> &'static RawJson {
use parking_lot::Mutex;
use std::mem::transmute;
static BUF: Mutex<Vec<Pin<Box<RawJson>>>> = Mutex::new(Vec::new());
let buf = BUF.lock();
let raw_json: Pin<Box<RawJson>> = raw_json.into();
let pt: &'static RawJson = {
let pt: &RawJson = &*raw_json;
transmute(pt)
};
buf.push(raw_json);
pt
}
If performance is not an issue, then one can deserialize the dynamic parts into the Value.
Similarly, if using Value is an option, then custom deserialization can be simplified by implementing TryFrom<Value>.
Serde supports internally tagged and untagged enums as of version 0.9.6.
The following code shows an example of how this could be done by using an enum with the attribute #[serde(untagged)].
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde_json; // 1.0.24
#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
error: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
age: i32,
name: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AgeOrError {
Age(MyAge),
Error(MyError),
}
fn get_results(ages: Vec<i32>) -> Vec<AgeOrError> {
let mut results = Vec::with_capacity(ages.len());
for age in ages {
if age < 100 && age > 0 {
results.push(AgeOrError::Age(MyAge {
age: age,
name: String::from("The dude"),
}));
} else {
results.push(AgeOrError::Error(MyError {
error: format!("{} is invalid age", age),
}));
}
}
results
}
fn main() {
let results = get_results(vec![1, -6, 7]);
let json = serde_json::to_string(&results).unwrap();
println!("{}", json);
}
The above code outputs the following JSON:
[{"age":1,"name":"The dude"},{"error":"-6 is invalid age"},{"age":7,"name":"The dude"}]
More information on Serde's enum representation can be found in the overview.

How can I use Serde with a JSON array with different objects for successes and errors?

I want to use Serde to create an array with error messages as well as proper objects:
extern crate serde; // 1.0.70
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde_json; // 1.0.24
#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
error: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
age: i32,
name: String,
}
fn get_results(ages: Vec<i32>) -> Vec<MyAge> {
let mut results = vec![];
for age in ages {
if age < 100 && age > 0 {
results.push(MyAge {
age: age,
name: String::from("The dude"),
});
} else {
results.push(MyError {
error: String::from(format!("{} is invalid age", age)),
});
}
}
results
}
When I pass in the Vec [1, -6, 7] I want to serialize to the JSON:
[{"age": 1, "name": "The dude"},{"error": "-6 is invalid age"},{"age": 7, "name": "The dude"}]
How do I do that? Knowing how to deserialize such an array would be nice as well.
Here's one way of doing that:
#[macro_use]
extern crate serde_derive; // 1.0.117
extern crate serde; // 1.0.117
extern crate serde_json; // 1.0.59
#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
error: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
age: i32,
name: String,
}
#[derive(Debug)]
enum AgeOrError {
Age(MyAge),
Error(MyError),
}
impl serde::Serialize for AgeOrError {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
&AgeOrError::Age(ref my_age) => serializer.serialize_some(my_age),
&AgeOrError::Error(ref my_error) => serializer.serialize_some(my_error),
}
}
}
enum AgeOrErrorField {
Age,
Name,
Error,
}
impl<'de> serde::Deserialize<'de> for AgeOrErrorField {
fn deserialize<D>(deserializer: D) -> Result<AgeOrErrorField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct AgeOrErrorFieldVisitor;
impl<'de> serde::de::Visitor<'de> for AgeOrErrorFieldVisitor {
type Value = AgeOrErrorField;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "age or error")
}
fn visit_str<E>(self, value: &str) -> Result<AgeOrErrorField, E>
where
E: serde::de::Error,
{
Ok(match value {
"age" => AgeOrErrorField::Age,
"name" => AgeOrErrorField::Name,
"error" => AgeOrErrorField::Error,
_ => panic!("Unexpected field name: {}", value),
})
}
}
deserializer.deserialize_any(AgeOrErrorFieldVisitor)
}
}
impl<'de> serde::Deserialize<'de> for AgeOrError {
fn deserialize<D>(deserializer: D) -> Result<AgeOrError, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_map(AgeOrErrorVisitor)
}
}
struct AgeOrErrorVisitor;
impl<'de> serde::de::Visitor<'de> for AgeOrErrorVisitor {
type Value = AgeOrError;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "age or error")
}
fn visit_map<A>(self, mut map: A) -> Result<AgeOrError, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut age: Option<i32> = None;
let mut name: Option<String> = None;
let mut error: Option<String> = None;
loop {
match map.next_key()? {
Some(AgeOrErrorField::Age) => age = map.next_value()?,
Some(AgeOrErrorField::Name) => name = map.next_value()?,
Some(AgeOrErrorField::Error) => error = map.next_value()?,
None => break,
}
}
if let Some(error) = error {
Ok(AgeOrError::Error(MyError { error: error }))
} else {
Ok(AgeOrError::Age(MyAge {
age: age.expect("!age"),
name: name.expect("!name"),
}))
}
}
}
fn get_results(ages: &[i32]) -> Vec<AgeOrError> {
let mut results = Vec::with_capacity(ages.len());
for &age in ages.iter() {
if age < 100 && age > 0 {
results.push(AgeOrError::Age(MyAge {
age: age,
name: String::from("The dude"),
}));
} else {
results.push(AgeOrError::Error(MyError {
error: format!("{} is invalid age", age),
}));
}
}
results
}
fn main() {
let v = get_results(&[1, -6, 7]);
let serialized = serde_json::to_string(&v).expect("Can't serialize");
println!("serialized: {}", serialized);
let deserialized: Vec<AgeOrError> =
serde_json::from_str(&serialized).expect("Can't deserialize");
println!("deserialized: {:?}", deserialized);
}
Note that in deserialization we can't reuse the automatically generated deserializers because:
deserialization is kind of streaming the fields to us, we can't peek into the stringified JSON representation and guess what it is;
we don't have access to the serde::de::Visitor implementations that Serde generates.
Also I did a shortcut and panicked on errors. In production code you'd want to return the proper Serde errors instead.
Another solution would be to make a merged structure with all fields optional, like this:
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde; // 1.0.70
extern crate serde_json; // 1.0.24
#[derive(Debug)]
pub struct MyError {
error: String,
}
#[derive(Debug)]
pub struct MyAge {
age: i32,
name: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAgeOrError {
#[serde(skip_serializing_if = "Option::is_none")]
age: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
impl MyAgeOrError {
fn from_age(age: MyAge) -> MyAgeOrError {
MyAgeOrError {
age: Some(age.age),
name: Some(age.name),
error: None,
}
}
fn from_error(error: MyError) -> MyAgeOrError {
MyAgeOrError {
age: None,
name: None,
error: Some(error.error),
}
}
}
fn get_results(ages: &[i32]) -> Vec<MyAgeOrError> {
let mut results = Vec::with_capacity(ages.len());
for &age in ages.iter() {
if age < 100 && age > 0 {
results.push(MyAgeOrError::from_age(MyAge {
age: age,
name: String::from("The dude"),
}));
} else {
results.push(MyAgeOrError::from_error(MyError {
error: format!("{} is invalid age", age),
}));
}
}
results
}
fn main() {
let v = get_results(&[1, -6, 7]);
let serialized = serde_json::to_string(&v).expect("Can't serialize");
println!("serialized: {}", serialized);
let deserialized: Vec<MyAgeOrError> =
serde_json::from_str(&serialized).expect("Can't deserialize");
println!("deserialized: {:?}", deserialized);
}
I'd vouch for this one because it allows the Rust structure (e.g. MyAgeOrError) to match the layout of your JSON. That way the JSON layout becomes documented in the Rust code.
P.S. Lately I tend to delay the decoding of optional or dynamically typed JSON parts with the help of RawValue. It's tricky to serialize them though, because RawValue is a borrow. For instance, and to help with serialization, one can intern a RawValue, promoting it to the 'static lifetime:
use serde_json::value::{RawValue as RawJson};
fn intern_raw_json(raw_json: Box<RawJson>) -> &'static RawJson {
use parking_lot::Mutex;
use std::mem::transmute;
static BUF: Mutex<Vec<Pin<Box<RawJson>>>> = Mutex::new(Vec::new());
let buf = BUF.lock();
let raw_json: Pin<Box<RawJson>> = raw_json.into();
let pt: &'static RawJson = {
let pt: &RawJson = &*raw_json;
transmute(pt)
};
buf.push(raw_json);
pt
}
If performance is not an issue, then one can deserialize the dynamic parts into the Value.
Similarly, if using Value is an option, then custom deserialization can be simplified by implementing TryFrom<Value>.
Serde supports internally tagged and untagged enums as of version 0.9.6.
The following code shows an example of how this could be done by using an enum with the attribute #[serde(untagged)].
#[macro_use]
extern crate serde_derive; // 1.0.70
extern crate serde_json; // 1.0.24
#[derive(Serialize, Deserialize, Debug)]
pub struct MyError {
error: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MyAge {
age: i32,
name: String,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AgeOrError {
Age(MyAge),
Error(MyError),
}
fn get_results(ages: Vec<i32>) -> Vec<AgeOrError> {
let mut results = Vec::with_capacity(ages.len());
for age in ages {
if age < 100 && age > 0 {
results.push(AgeOrError::Age(MyAge {
age: age,
name: String::from("The dude"),
}));
} else {
results.push(AgeOrError::Error(MyError {
error: format!("{} is invalid age", age),
}));
}
}
results
}
fn main() {
let results = get_results(vec![1, -6, 7]);
let json = serde_json::to_string(&results).unwrap();
println!("{}", json);
}
The above code outputs the following JSON:
[{"age":1,"name":"The dude"},{"error":"-6 is invalid age"},{"age":7,"name":"The dude"}]
More information on Serde's enum representation can be found in the overview.