json-api-rs icon indicating copy to clipboard operation
json-api-rs copied to clipboard

Examples to deserialize a resource from json

Open ttdonovan opened this issue 6 years ago • 6 comments

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?

ttdonovan avatar Mar 23 '18 19:03 ttdonovan

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

ttdonovan avatar Mar 23 '18 22:03 ttdonovan

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());
}

zacharygolba avatar Mar 25 '18 23:03 zacharygolba

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: "🛡"
    //     }
    // ]
}

zacharygolba avatar Mar 25 '18 23:03 zacharygolba

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 .

ttdonovan avatar Mar 26 '18 00:03 ttdonovan

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 avatar Jun 13 '19 01:06 Abdillah

@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. :-)

zacharygolba avatar Jun 13 '19 14:06 zacharygolba