Serde json typed deserialize error handling -- return or wrap the error

#1

EDIT: Sorry, I submitted before it was finished. My cat hopped on the keyboard, lol.

This is probably a simple question, and I am sure there is a more idiomatic way to structure my code. I would love to hear the more idiomatic way, but I’d also like an answer to my question since it may apply in other situations.

I am using serde_json to deserialize a json file into a strongly typed Game struct. All is well.

However, I want to handle a deserialization error explicitly, and return or wrap a custom error type (InvalidGameManifest), but I can’t figure out how to actually get an error without a panic from the from_str() method.

Here’s what I mean:

pub fn load(manifest_dir: String) -> Result<Game, InvalidGameManifest> {
    // Get the file path
    let game_manifest_file = format!("{}/.game.json", manifest_dir);

    // Get the contents of the file
    let game_manifest = read_file(&game_manifest_file);

    // This contents will go through a json schema validator, to give more explicit errors about json contents
    // These errors will be returned as part of an `InvalidGameManifest` error

    // Now, build the Game object using serde
    if let Ok(game) = hydrate(&game_manifest) {
        Ok(game)
    } else {
        Err(InvalidGameManifest)
    }
}

pub fn hydrate(contents: &String) -> Result<Game, serde_json::Error> {
    let game: Game = serde_json::from_str(contents)?;
    game
}

This code does not compile because I want to return a Result with the Serde Json error, so I can “catch” it and wrap it. But, the compiler believes that Game can’t be a Result.

So, how can I return a Serde Result from a method when using a strongly typed deserializor.

#2

This is not good, if you care about cross-plaftorm, use std::path::PathBuf and join method

To map err conversation you can use map_err

pub fn hydrate(contents: &String) -> Result<Game, MyCustomError> {
let game: Game = serde_json::from_str(contents).map_err(|err: serde_json::Error| {
  MyCustomError
})?;
1 Like
#3

@Dushistov, well that is just exactly what I was looking for. Thanks a lot. And thanks for the extra bit about PathBuf.

#4

@Dushistov, actually, I am still getting a mismatched type error.

error[E0308]: mismatched types
  --> src/manifests/game.rs:23:5
   |
18 | pub fn hydrate(contents: &String) -> Result<Game, InvalidGameManifest> {
   |                                      --------------------------------- expected `std::result::Result<game::Game, manifests::errors::InvalidGameManifest>` because of return type
...
23 |     game
   |     ^^^^ expected enum `std::result::Result`, found struct `game::Game`
   |
   = note: expected type `std::result::Result<game::Game, manifests::errors::InvalidGameManifest>`
              found type `game::Game`
#5

If you’re returning the Result (and that’s the way to get the error passed to caller), you must wrap your final return value in Ok: Ok(game) should be just fine.

2 Likes
#6

@Cerberuser That fixed it for me. Thank you!