2015-10-29 23 views
9

Próbuję deserializować JSON do struktury Rust za pomocą rustc_serialize. Problem polega na tym, że niektóre JSON mają pewne opcjonalne pola, tj. Mogą być obecne lub nie. W momencie napotkania pierwszego nieobecnego pola dekoder wydaje się wyskakiwać i nie uwzględniać kolejnych pól, nawet jeśli są obecne. Czy istnieje sposób na pokonanie tego?Nie można rozwiązać opcjonalnych pól w JSON z Rustc-serialize

Oto kod:

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

i tu jest wyjście:

-------------------------------- 
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 } 

Zauważ, że trzeci bieg daje nieoczekiwane rezultaty. Gdy dekoder nie znajdzie numeru some_field_0, zawiedzie on na wszystkich kolejnych tokenach, mimo że występuje w nim some_field_1.

+1

Wygląda jak błąd w seriach rustc. Rozważ złożenie problemu na [github] (https://github.com/rust-lang-nursery/rustc-serialize) – aochagavia

Odpowiedz

6

Coś jest nie tak z Twoją implementacją Decodable. Korzystanie z automatycznie wygenerowaną implementację działa:

#[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 } 

Korzystanie wygenerowanej implementacji jest słuszne jeśli możesz. Jeśli nie, oto prawo realizacja:

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))), 
     }) 
    } 
} 

Ważną zmianą jest zastosowanie try!. Dekodowanie może się nie udać. Używając ok, mówisz, że nieudane dekodowanie było w rzeczywistości wynikiem powodzenia, aczkolwiek udane dekodowanie None.

+0

Muszę być pod wpływem narkotyków, aby sądzić, że wygenerowana implementacja nie będzie działać: D .. ale jeśli zrobimy przejść z wprowadzoną ręczną implementacją nie "spróbujesz!" spowoduje wczesny powrót? Nie tego chciałem - musiałem umieścić to jako "Brak" i kontynuować sprawdzanie następnego pola. Nie spojrzałem jeszcze na tę ładnie rozwiniętą wersję, chociaż widzę, jakie domyślne ustawienia sprawiają, że działa. – ustulation

+0

Tak samo jak próba odpowiedzi na powyższy komentarz: Musi istnieć specjalizacja dla opcji "Opcja ", aby móc rozróżnić dekodowanie na 'T', za które wyskoczy i nie zrobi nic więcej oraz' Opcja 'for który będzie bardziej łagodny i zwróci 'None', jeśli pole nie zostanie znalezione, ale nie zostanie wyniesione i osiągnie EOF (ale w przeciwnym razie to samo zachowanie dla każdego innego błędu). Jestem tam blisko? – ustulation