2016-06-01 7 views
5

Ich möchte ein Array mit Fehlermeldungen sowie ordnungsgemäße Objekte erstellen.Rust Serde JSON-Array mit verschiedenen Objekten/Fehlern?

#[derive(Serialize, Deserialize, Debug)] 
pub struct MyError { 
    error: String 
} 

#[derive(Serialize, Deserialize, Debug)] 
pub struct MyAge { 
    age: i32, 
    name: String 
} 

fn get_results(ages: Vec<i32>) -> Vec<MyAge> { 
    let mut results = vec![]; 
    for age in ages { 
     if age < 100 && age > 0 { 
      results.push(MyAge{age: age, name: String::from("The dude")}); 
     } else { 
      results.push(MyError{error: String::from(format!("{} is invalid age", age)) }); 
     } 
    } 
    results 
} 

Ich benutze Serde, um den JSON zu serialisieren. Als ich in der Vec [1,-6,7] geben möchte ich ein Array, das auf die JSON in serde serialisiert:

[{"age": 1, "name": "The dude"},{"error": "-6 is invalid age"},{"age": 7, "name": "The dude"}] 

Wie kann ich das tun? Zu wissen, wie man ein solches Array deserialisiert, wäre auch schön.

+1

Bitte zeigen Sie, was es derzeit serialisiert. –

+0

@ker Es wird nicht zu etwas searialisieren. Dies ist kein gültiger Code. Weil "MyError" und "MyAge" nicht kompatible Typen sind. – user3384741

+1

oooh ... Hast du es stattdessen mit einem Enum versucht? Es gibt eine Million Möglichkeiten, um Ihr Problem zu lösen. Die Lösungen haben unterschiedliche Mengen und Arten von Code, die Sie selbst schreiben müssen ... Sind Sie an das genaue Ausgabeformat gebunden? –

Antwort

8

Nun, hier ist ein Weg, dies zu tun:

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

#[derive(Serialize, Deserialize, Debug)] 
pub struct MyError { 
    error: String, 
} 

#[derive(Serialize, Deserialize, Debug)] 
pub struct MyAge { 
    age: i32, 
    name: String, 
} 

#[derive(Debug)] 
enum AgeOrError { 
    Age(MyAge), 
    Error(MyError), 
} 

impl serde::Serialize for AgeOrError { 
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 
     match self { 
      &AgeOrError::Age(ref my_age) => serializer.serialize_some(my_age), 
      &AgeOrError::Error(ref my_error) => serializer.serialize_some(my_error), 
     } 
    } 
} 

enum AgeOrErrorField { 
    Age, 
    Name, 
    Error, 
} 
impl serde::Deserialize for AgeOrErrorField { 
    fn deserialize<D>(deserializer: D) -> Result<AgeOrErrorField, D::Error> 
     where D: serde::Deserializer 
    { 
     struct AgeOrErrorFieldVisitor; 
     impl serde::de::Visitor for AgeOrErrorFieldVisitor { 
      type Value = AgeOrErrorField; 
      fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 
       write!(formatter, "age or error") 
      } 
      fn visit_str<E>(self, value: &str) -> Result<AgeOrErrorField, E> 
       where E: serde::de::Error 
      { 
       Ok(match value { 
        "age" => AgeOrErrorField::Age, 
        "name" => AgeOrErrorField::Name, 
        "error" => AgeOrErrorField::Error, 
        _ => panic!("Unexpected field name: {}", value), 
       }) 
      } 
     } 
     deserializer.deserialize(AgeOrErrorFieldVisitor) 
    } 
} 

impl serde::Deserialize for AgeOrError { 
    fn deserialize<D>(deserializer: D) -> Result<AgeOrError, D::Error> 
     where D: serde::Deserializer 
    { 
     deserializer.deserialize_map(AgeOrErrorVisitor) 
    } 
} 
struct AgeOrErrorVisitor; 
impl serde::de::Visitor for AgeOrErrorVisitor { 
    type Value = AgeOrError; 
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 
     write!(formatter, "age or error") 
    } 
    fn visit_map<V>(self, mut visitor: V) -> Result<AgeOrError, V::Error> 
     where V: serde::de::MapVisitor 
    { 
     let mut age: Option<i32> = None; 
     let mut name: Option<String> = None; 
     let mut error: Option<String> = None; 
     loop { 
      match try!(visitor.visit_key()) { 
       Some(AgeOrErrorField::Age) => age = try!(visitor.visit_value()), 
       Some(AgeOrErrorField::Name) => name = try!(visitor.visit_value()), 
       Some(AgeOrErrorField::Error) => error = try!(visitor.visit_value()), 
       None => break, 
      } 
     } 
     if let Some(error) = error { 
      Ok(AgeOrError::Error(MyError { error: error })) 
     } else { 
      Ok(AgeOrError::Age(MyAge { 
       age: age.expect("!age"), 
       name: name.expect("!name"), 
      })) 
     } 
    } 
} 

fn get_results(ages: &[i32]) -> Vec<AgeOrError> { 
    let mut results = Vec::with_capacity(ages.len()); 
    for &age in ages.iter() { 
     if age < 100 && age > 0 { 
      results.push(AgeOrError::Age(MyAge { 
       age: age, 
       name: String::from("The dude"), 
      })); 
     } else { 
      results.push(AgeOrError::Error(MyError { error: format!("{} is invalid age", age) })); 
     } 
    } 
    results 
} 

pub fn main() { 
    let v = get_results(&[1, -6, 7]); 
    let serialized = serde_json::to_string(&v).expect("Can't serialize"); 
    println!("serialized: {}", serialized); 
    let deserialized: Vec<AgeOrError> = serde_json::from_str(&serialized) 
     .expect("Can't deserialize"); 
    println!("deserialized: {:?}", deserialized); 
} 

Beachten Sie, dass in der Deserialisierung können wir nicht die automatisch generierten Deserializer wiederverwenden, weil:
a) Deserialisierung Art streamt die Felder zu uns, wir kann nicht peek in die stringifizierte JSON-Darstellung und raten Sie, was es ist;
b) Wir haben keinen Zugriff auf die serde::de::Visitor Implementierungen, die Serde generiert.

Auch ich habe eine Abkürzung und panick Ed auf Fehler. Im Produktionscode möchten Sie stattdessen die richtigen Serde-Fehler zurückgeben.


Eine andere Lösung wäre, eine fusionierte Struktur mit allen Feldern wie diese optional zu machen:

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

#[derive(Debug)] 
pub struct MyError { 
    error: String, 
} 

#[derive(Debug)] 
pub struct MyAge { 
    age: i32, 
    name: String, 
} 

#[derive(Serialize, Deserialize, Debug)] 
pub struct MyAgeOrError { 
    #[serde(skip_serializing_if="Option::is_none")] 
    age: Option<i32>, 
    #[serde(skip_serializing_if="Option::is_none")] 
    name: Option<String>, 
    #[serde(skip_serializing_if="Option::is_none")] 
    error: Option<String>, 
} 
impl MyAgeOrError { 
    fn from_age(age: MyAge) -> MyAgeOrError { 
     MyAgeOrError { 
      age: Some(age.age), 
      name: Some(age.name), 
      error: None, 
     } 
    } 
    fn from_error(error: MyError) -> MyAgeOrError { 
     MyAgeOrError { 
      age: None, 
      name: None, 
      error: Some(error.error), 
     } 
    } 
} 

fn get_results(ages: &[i32]) -> Vec<MyAgeOrError> { 
    let mut results = Vec::with_capacity(ages.len()); 
    for &age in ages.iter() { 
     if age < 100 && age > 0 { 
      results.push(MyAgeOrError::from_age(MyAge { 
       age: age, 
       name: String::from("The dude"), 
      })); 
     } else { 
      results.push(MyAgeOrError::from_error(MyError { 
       error: format!("{} is invalid age", age), 
      })); 
     } 
    } 
    results 
} 

pub fn main() { 
    let v = get_results(&[1, -6, 7]); 
    let serialized = serde_json::to_string(&v).expect("Can't serialize"); 
    println!("serialized: {}", serialized); 
    let deserialized: Vec<MyAgeOrError> = serde_json::from_str(&serialized) 
     .expect("Can't deserialize"); 
    println!("deserialized: {:?}", deserialized); 
} 

ich für diese bürgen würde, weil es die Struktur Rust ermöglicht (zB) übereinstimmen das Layout Ihres JSON. Auf diese Weise wird das JSON-Layout im Rust-Code dokumentiert.

5

Serde unterstützt intern markierte und unmarkierte Enums ab Version 0.9.6.

Der folgende Code zeigt ein Beispiel dafür, wie dies mithilfe einer Enumeration mit dem Attribut #[serde(untagged)] durchgeführt werden kann.

#[macro_use] 
extern crate serde_derive; 

extern crate serde_json; 


#[derive(Serialize, Deserialize, Debug)] 
pub struct MyError { 
    error: String 
} 

#[derive(Serialize, Deserialize, Debug)] 
pub struct MyAge { 
    age: i32, 
    name: String 
} 

#[derive(Serialize, Deserialize, Debug)] 
#[serde(untagged)] 
pub enum AgeOrError { 
    Age(MyAge), 
    Error(MyError), 
} 

fn get_results(ages: Vec<i32>) -> Vec<AgeOrError> { 
    let mut results = Vec::with_capacity(ages.len()); 
    for age in ages { 
     if age < 100 && age > 0 { 
      results.push(AgeOrError::Age(MyAge { 
       age: age, 
       name: String::from("The dude") 
      })); 
     } else { 
      results.push(AgeOrError::Error(MyError { 
       error: String::from(format!("{} is invalid age", age)) 
      })); 
     } 
    } 
    results 
} 

fn main() { 
    let results = get_results(vec![1, -6, 7]); 
    let json = serde_json::to_string(&results).unwrap(); 
    println!("{}", json); 
} 

Der obige Code gibt die folgende JSON:

[{"age":1,"name":"The dude"},{"error":"-6 is invalid age"},{"age":7,"name":"The dude"}] 

Mehr Informationen über Serde ENUM Darstellung kann in der overview finden.