I am trying to deserialize JSON to Rust structure using rustc_serialize. The problem is that certain JSONs have some optional fields, i.e., may or may not be present. The moment the first absent field is encountered, the decoder seems to bail out and not consider subsequent fields, even if they are present. Is there a way to overcome this?
Here is the code:
extern crate rustc_serialize;
#[derive(Debug)]
struct B {
some_field_0: Option<u64>,
some_field_1: Option<String>,
}
impl rustc_serialize::Decodable for B {
fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
Ok(B {
some_field_0: d.read_struct_field("some_field_0", 0, |d| rustc_serialize::Decodable::decode(d)).ok(),
some_field_1: d.read_struct_field("some_field_1", 0, |d| rustc_serialize::Decodable::decode(d)).ok(),
})
}
}
fn main() {
{
println!("--------------------------------\n1st run - all field present\n--------------------------------");
let json_str = "{\"some_field_0\": 1234, \"some_field_1\": \"There\"}".to_string();
let obj_b: B = rustc_serialize::json::decode(&json_str).unwrap();
println!("\nJSON: {}\nDecoded: {:?}", json_str, obj_b);
}
{
println!("\n\n--------------------------------\n2nd run - \"some_field_1\" absent\n---------------------------------");
let json_str = "{\"some_field_0\": 1234}".to_string();
let obj_b: B = rustc_serialize::json::decode(&json_str).unwrap();
println!("\nJSON: {}\nDecoded: {:?}", json_str, obj_b);
}
{
println!("\n\n--------------------------------\n3rd run - \"some_field_0\" absent\n---------------------------------");
let json_str = "{\"some_field_1\": \"There\"}".to_string();
let obj_b: B = rustc_serialize::json::decode(&json_str).unwrap();
println!("\nJSON: {}\nDecoded: {:?}", json_str, obj_b);
}
}
and here's the output:
--------------------------------
1st run - all field present
--------------------------------
JSON: {"some_field_0": 1234, "some_field_1": "There"}
Decoded: B { some_field_0: Some(1234), some_field_1: Some("There") }
--------------------------------
2nd run - "some_field_1" absent
---------------------------------
JSON: {"some_field_0": 1234}
Decoded: B { some_field_0: Some(1234), some_field_1: None }
--------------------------------
3rd run - "some_field_0" absent
---------------------------------
JSON: {"some_field_1": "There"}
Decoded: B { some_field_0: None, some_field_1: None }
Notice that the third run produces an unexpected result. When the decoder fails to find some_field_0
it fails on all subsequent tokens, even though some_field_1
is present.
There's something wrong with your Decodable
implementation. Using the automatically-generated implementation works:
#[derive(Debug, RustcDecodable)]
struct B {
some_field_1: Option<String>,
some_field_0: Option<u64>,
}
JSON: {"some_field_1": "There"}
Decoded: B { some_field_1: Some("There"), some_field_0: None }
Using the generated implementation is the right thing to do if you can. If you cannot, here's the right implementation:
impl rustc_serialize::Decodable for B {
fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
Ok(B {
some_field_0: try!(d.read_struct_field("some_field_0", 0, |d| rustc_serialize::Decodable::decode(d))),
some_field_1: try!(d.read_struct_field("some_field_1", 0, |d| rustc_serialize::Decodable::decode(d))),
})
}
}
The important change is the use of try!
. Decoding can fail. By using ok
, you were saying that a failed decoding was actually a success, albeit a successful decoding of a None
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With