[solved] Rustc_serialize custom decode() - how to return custom error


#1

Hi,

I’m implementing an application that interacts with an existing json web service and have some problems with returning custom errors.
The challenge is that the json returned by the web service contains optional fields which may not exist.
I use the rustc_serialize crate but due to the optional fields I can’t use #[derive(RustcDecodable)] because it would expect those fields. (?)
Therefore, I’m currently implementing the Decodable Trait:

    struct A {
        mandatory: String,
        optional: Option<String>,
    }

    impl rustc_serialize::Decodable for A {
        fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
            d.read_map(|s, len| {
                let mandatory: Option<String> = None;
                let optional: Option<String> = None;
                for x in 1..len {
                        let key = try!(s.read_map_elt_key(x-1, |s| s.read_str())).as_ref(); 
                        match key {
                                "mandatory" => mandatory = Some(try!(s.read_map_elt_val(x-1, |s| s.read_str()))),
                                "optional" => optional = Some(try!(s.read_map_elt_val(x-1, |s| s.read_str()))),
                                _ => {
                                    let msg = String::from("Unexpected key: ");
                                    msg.push_str(key);
                                    return Err(rustc_serialize::json::DecoderError::UnknownVariantError(msg));
                                }
                        }
                }
                Ok(A {
                        mandatory: mandatory.unwrap(),
                        optional: optional,
                })
            })
        }
    }

This won’t compile:

src/lib.rs:173 		return Err(rustc_serialize::json::DecoderError::UnknownVariantError(msg));
               			   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:173:20: 173:81 help: run `rustc --explain E0308` to see a detailed explanation
src/lib.rs:173:20: 173:81 note: expected type `<D as rustc_serialize::Decoder>::Error`
src/lib.rs:173:20: 173:81 note:    found type `rustc_serialize::json::DecoderError`

I understand the issue. D::Error is an unbound associated type and rustc_serialize::json::DecoderError::UnknownVariantError(msg) is something completely different.

Specifying that I expect Error to be of that type conflicts with the Trait implementation:

fn decode<D: rustc_serialize::Decoder<Error=rustc_serialize::json::DecoderError>>(d: &mut D) -> Result<Self, D::Error>

But how am I supposed to return my ‘Unknown key’ error while still implementing the Trait then?


#2

Just as an alternative, have you considered that perhaps you don’t need to implement Decodable yourself at all? When your RustcDecodable struct contains an Option<T>, rustc_serialize will set that field to None if it did not decode the corresponding key from the JSON provided. Consider the following example.

extern crate rustc_serialize;

use rustc_serialize::{Decodable, json};

#[derive(RustcDecodable, Debug)]
struct A {
    mandatory: String,
    optional: Option<String>
}

fn main() {
    let json_str = "{\"mandatory\": \"foo\"}";
    let a: A = json::decode(json_str).unwrap();
    println!("{:?}", a);
}

When I run this with plain old cargo run, I get the following output:

A { mandatory: "foo", optional: None }

What you might thus consider is writing your own function to decode a JSON string into your struct (which just calls json::decode) and have that function test for any None values in the struct’s fields, indicating a missing key.

I’m sorry if that answer isn’t sufficient for you right now. I can have a look into your more specific question about returning a specific error if this won’t do.


#3

That is pretty neat. I was not aware that RustcDecodable works like this.

In the meantime I found out that Decoder implements an error method one can use to create the expected error type.