2015-10-29 16 views
9

Sto tentando di deserializzare la struttura JSON in Rust usando rustc_serialize. Il problema è che alcuni JSON hanno alcuni campi opzionali, vale a dire possono o non possono essere presenti. Nel momento in cui si incontra il primo campo assente, il decoder sembra salvare e non considerare i campi successivi, anche se sono presenti. C'è un modo per superare questo?Impossibile affrontare i campi opzionali in JSON con serializzazione Rustc

Ecco il codice:

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

e ecco l'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 } 

Si noti che il terzo periodo produce un risultato inaspettato. Quando il decodificatore non riesce a trovare some_field_0 non riesce su tutti i token successivi, anche se è presente some_field_1.

+1

Sembra un errore in serial-serial. Considerare l'archiviazione di un problema su [github] (https://github.com/rust-lang-nursery/rustc-serialize) – aochagavia

risposta

6

C'è qualcosa di sbagliato nell'implementazione dello Decodable. Utilizzando l'applicazione generato automaticamente funziona:

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

usando l'implementazione generato è la cosa giusta da fare se potete. Se non è possibile, ecco la giusta applicazione:

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

Il cambiamento importante è l'uso di try!. La decodifica può fallire. Utilizzando ok, si stava dicendo che una decodifica fallita era in realtà un successo , anche se una decodifica riuscita di un None.

+0

devo essere stato drogato per pensare che l'implementazione generata non avrebbe funzionato: D .. ma se lo facciamo andare con l'implementazione manuale che hai postato non proverebbe! Provoca un ritorno anticipato? Non è quello che volevo - dovevo metterlo come "Nessuno" e continuare a ispezionare il campo successivo. Non ho ancora guardato quella versione abbastanza espansa, anche se per vedere quale default lo fa funzionare. – ustulation

+0

Proprio come un tentativo di rispondere al mio commento sopra: Ci deve essere una specializzazione per "Opzione " in modo che possa distinguere tra la decodifica di un 'T' per il quale verrà salvato e non fare nulla di più e' Opzione ' per che sarà più clemente e restituirà 'None' se il campo non viene trovato ma non viene salvato e raggiunge EOF (ma altrimenti lo stesso comportamento per qualsiasi altro errore). Sono lì vicino? – ustulation