json-api-rs
json-api-rs copied to clipboard
Examples to deserialize a resource from json
Maybe I'm overlooking something very obvious (still a beginner with Rust) but I am having a hard time understanding how to deserialize a resource. I tried looking for test coverage but it appears that the doc
is 0.0%. Would it be possible to include an example in the README.md
showing how to deserialize from a JSON string using doc::from_str
for the Post resource?
I've created a test branch to experiment and demonstrate the issue.
https://github.com/ttdonovan/json-api-rs/tree/tests-doc-from-str
cargo test -- --nocapture doc_from_str
Hey thanks for your interest in this library! 😃
There are 2 small issues I spotted in your tests-doc-from-str
branch. No worries though! I wrote some examples that should help explain how to move forward. Also it's worth noting that this crate is pretty low-level by design. I think a small layer of abstraction is needed to use this library painlessly inside of a an application.
The first issue I saw was that the Deserialize
trait is not implemented for the Person
struct. This is required to be able to convert the intermediate Document
type into a Person
.
Before
#[macro_use]
extern crate json_api;
struct Person {
id: String,
name: String,
symbol: String,
}
After
#[macro_use]
extern crate json_api;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[derive(Debug, Deserialize, Serialize)]
struct Person {
id: String,
name: String,
symbol: String,
}
The second is that json_api::doc::from_str
relies on generic parameters to determine what type of document it is deserializing. In order to solve this problem, we have to help rustc
fill in the blanks.
Before
use json_api::doc::{self, Object};
fn main() {
//
// error[E0283]: type annotations required: cannot resolve `_: json_api::doc::PrimaryData`
//
let heroes = doc::from_str("...");
// ^^^^^^^^^^^^^
// note: required by `json_api::from_str`
//
println!("{:#?}", heroes.unwrap());
}
After
use json_api::doc::{self, Object};
fn main() {
let heroes = doc::from_str::<Object, Vec<Person>>("...");
// ^^^^^^ ^^^^^^^^^^^
//
// * Notice the two generic type parameters above
//
// The first parameter indicates the type of primary data the document contains.
//
// For example, should the document be validated against the resource
// object or resource identifier schema.
//
// The second parameter is the type that is forwarded to serde after the document
// is validated and flattened.
//
println!("{:#?}", heroes.unwrap());
}
Adding Sugar
A good pattern for use in an application would be to disambiguate the generic types as much as possible. This allows rustc
to infer the types and makes calling library functions less of a pain.
use json_api::doc::{self, Object};
use json_api::Error;
fn heroes_from_objects(input: &str) -> Result<Vec<Person>, Error> {
doc::from_str::<Object, _>(input)
// ^
// You can use a placeholder for the second type parameter since
// rustc can infer it from the function signature.
//
}
fn main() {
let heroes = heroes_from_objects("...");
// ^^^^^^^^^^^^^^^^^^^
// The `heroes_from_objects` is bound by it's function signature and
// disambiguated function call to only ever deserialize Vec<Person>
// from a document that contains one or more resource objects as it's
// primary data.
//
println!("{:#?}", heroes.unwrap());
}
Here is a complete working example that I tested locally w/ rust 1.24.1
. Let me know if you have any questions! 😃
#[macro_use]
extern crate json_api;
extern crate serde;
#[macro_use]
extern crate serde_derive;
use json_api::Error;
use json_api::doc::{self, Object};
#[derive(Debug, Deserialize, Serialize)]
struct Person {
id: String,
name: String,
symbol: String,
}
resource!(Person, |&self| {
kind "heroes";
id self.id;
attrs name, symbol;
});
fn main() {
let heroes = doc::from_str::<Object, Vec<Person>>(r#"{
"data": [{
"type": "heroes",
"id": "1",
"attributes": {
"name": "Batman",
"symbol": "🦇"
}
}, {
"type": "heroes",
"id": "2",
"attributes": {
"name": "Wonder Woman",
"symbol": "🛡"
}
}]
}"#);
println!("{:#?}", heroes.unwrap());
// [
// Person {
// id: "1",
// name: "Batman",
// symbol: "🦇"
// },
// Person {
// id: "2",
// name: "Wonder Woman",
// symbol: "🛡"
// }
// ]
}
Thanks for the example. I was getting closer but still didn’t have a working example. This should definitely help me.
On Sun, Mar 25, 2018 at 18:42 Zachary Golba [email protected] wrote:
Here is a complete working example that I tested locally w/ rust 1.24.1. Let me know if you have any questions! 😃
#[macro_use]extern crate json_api;extern crate serde; #[macro_use]extern crate serde_derive;
use json_api::Error;use json_api::doc::{self, Object};
#[derive(Debug, Deserialize, Serialize)]
struct Person { id: String, name: String, symbol: String, }
resource!(Person, |&self| { kind "heroes"; id self.id; attrs name, symbol; }); fn main() { let heroes = doc::from_str::<Object, Vec<Person>>(r#"{ "data": [{ "type": "heroes", "id": "1", "attributes": { "name": "Batman", "symbol": "🦇" } }, { "type": "heroes", "id": "2", "attributes": { "name": "Wonder Woman", "symbol": "🛡" } }] }"#);
println!("{:#?}", heroes.unwrap()); // [ // Person { // id: "1", // name: "Batman", // symbol: "🦇" // }, // Person { // id: "2", // name: "Wonder Woman", // symbol: "🛡" // } // ]
}
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/zacharygolba/json-api-rs/issues/32#issuecomment-376013276, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAo3DVJlZ7PvxlSln4p457sw_JBVZLYks5tiCtPgaJpZM4S5S-e .
It is fantastic to see this test be included in the repository. May I do a pull request? (Actually I prefer to help due to attribution)
I have created a simple assertion test locally.
@Abdillah that is a great idea!
May I do a pull request? (Actually I prefer to help due to attribution)
Yes, feel free to make a pull request. :-)