2017-06-01 45 views
5

Chcę serializować i deserializować chrono::NaiveDate z niestandardowymi funkcjami, ale książka Serde nie obejmuje tej funkcji, a dokumenty z kodem również nie pomagają.Jak można deserializować pole opcjonalne z funkcjami niestandardowymi przy użyciu Serde?

#[macro_use] 
extern crate serde_derive; 
extern crate serde; 
extern crate serde_json; 
extern crate chrono; 

use chrono::NaiveDate; 


mod date_serde { 
    use chrono::NaiveDate; 
    use serde::{self, Deserialize, Serializer, Deserializer}; 

    pub fn serialize<S>(date: &Option<NaiveDate>, s: S) -> Result<S::Ok, S::Error> 
    where S: Serializer { 
     if let Some(ref d) = *date { 
      return s.serialize_str(&d.format("%Y-%m-%d").to_string()) 
     } 
     s.serialize_none() 
    } 

    pub fn deserialize<'de, D>(deserializer: D) 
     -> Result<Option<NaiveDate>, D::Error> 
     where D: Deserializer<'de> { 
     let s: Option<String> = Option::deserialize(deserializer)?; 
     if let Some(s) = s { 
      return Ok(Some(NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(serde::de::Error::custom)?)) 
     } 

     Ok(None) 
    } 
} 

#[derive(Debug, Serialize, Deserialize)] 
struct Test { 
    pub i: u64, 
    #[serde(with = "date_serde")] 
    pub date: Option<NaiveDate>, 
} 

fn main() { 
    let mut test: Test = serde_json::from_str(r#"{"i": 3, "date": "2015-02-03"}"#).unwrap(); 
    assert_eq!(test.i, 3); 
    assert_eq!(test.date, Some(NaiveDate::from_ymd(2015, 02, 03))); 
    test = serde_json::from_str(r#"{"i": 5}"#).unwrap(); 
    assert_eq!(test.i, 5); 
    assert_eq!(test.date, None); 
} 

wiem, że Option<chrono::NaiveDate> można łatwo rozszeregować przez Serde bo Chrono posiada wsparcie Serde, ale próbuję nauczyć Serde więc chcę wdrożyć go samodzielnie. Kiedy uruchomić ten kod mam błąd:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ErrorImpl { code: Message("missing field `date`"), line: 1, column: 8 }', /checkout/src/libcore/result.rs:859 
note: Run with `RUST_BACKTRACE=1` for a backtrace. 

Odpowiedz

2

pole typu Option tutaj nie mówi Deserializator że pole może nie istnieć. Sugeruje raczej, że zawiera pole z pustą lub zerową zawartością (none w kategoriach Serde). Na przykład: serde_json jest odpowiednikiem języka JavaScript "albo null albo string" (null | string w notacji typuScript/przepływu). Ten kod poniżej działa dobrze:

let test: Test = serde_json::from_str(r#"{"i": 5, "date": null}"#).unwrap(); 
assert_eq!(test.i, 5); 
assert_eq!(test.date, None); 

Na szczęście, proces deserializacji może stać się bardziej liberalne po prostu przez dodanie atrybutu serde(default) (Option::default plony None):

#[derive(Debug, Serialize, Deserialize)] 
struct Test { 
    pub i: u64, 

    #[serde(default)] 
    #[serde(with = "date_serde")] 
    pub date: Option<NaiveDate>, 
} 

Playground

Zobacz również How to deserialize a JSON file which contains null values using Serde? (nie duplikat, ale zawiera podobny i stosowny przypadek użycia)

+1

Dlaczego jest prosty 'pub date: Opcja ' deserializuje się poprawnie, gdy pole 'date' było obecne? –

+0

Zostało to wyjaśnione w pierwszym akapicie. Czy możesz dokładniej określić, czego nie rozumiesz? –

+1

Przepraszam, literówka: ... deserializuje się dobrze, gdy pole 'date' nie było ** obecne. –