2017-07-12 5 views
1

Ich habe JSON, die zwei Formen annehmen:Wie wird die Deserialisierung in eine Enum-Variante basierend auf einem Schlüsselnamen durchgeführt?

"Example:" { "field": 42, "A": 76 } 
"Example:" { "field": 42, "B": 110 } 

ich es in eine Struktur wie diese deserialisiert wollen:

struct Example { 
    field: i32, 
    an_enum: AnEnum, 
} 

wo

enum AnEnum { 
    A(i32), 
    B(i32), 
} 

Ich weiß nicht, wie man Tun Sie es, ohne einen benutzerdefinierten Deserializer für Example zu schreiben.

Dies funktioniert:

"Example:" { "field": 42, "an_enum": {"A": 76} } 

oder in YAML:

Example: 
    field: 42 
    an_enum: 
     A: 76 

Die an_enum ist überflüssig und lästig zu schreiben. Wie kann ich die erste Form in die Struktur deserialisieren? Oder alternativ, wie kann ich eine Struktur deklarieren, die die gewünschte Syntax erfolgreich deserialisiert?

Antwort

2

Serde hat eine Reihe von möglichen enum representations, aber Ihre gewünschte Struktur ist nicht unter ihnen.

Statt dass, würde ich benutzerdefinierte Deserialisierung verwenden:

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

use serde::{Deserialize, Deserializer}; 
use serde::de::Error; 

#[derive(Debug)] 
struct Example { 
    field: i32, 
    an_enum: AnEnum, 
} 

#[derive(Debug)] 
enum AnEnum { 
    A(i32), 
    B(i32), 
} 

impl<'de> Deserialize<'de> for Example { 
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 
    where 
     D: Deserializer<'de>, 
    { 
     #[derive(Debug, Deserialize)] 
     struct Mapping { 
      field: i32, 
      #[serde(rename = "A")] 
      a: Option<i32>, 
      #[serde(rename = "B")] 
      b: Option<i32>, 
     } 

     let Mapping { field, a, b } = Mapping::deserialize(deserializer)?; 

     match (a, b) { 
      (Some(_), Some(_)) => 
       Err(D::Error::custom("multiple variants specified")), 
      (Some(a), None) => 
       Ok(Example { field, an_enum: AnEnum::A(a) }), 
      (None, Some(b)) => 
       Ok(Example { field, an_enum: AnEnum::B(b) }), 
      (None, None) => 
       Err(D::Error::custom("no variants specified")), 
     } 
    } 
} 

fn main() { 
    let a = r#"{ "field": 42, "A": 76 }"#; 
    let b = r#"{ "field": 42, "B": 110 }"#; 

    let a: Result<Example, _> = serde_json::from_str(a); 
    let b: Result<Example, _> = serde_json::from_str(b); 

    println!("{:?}", a); 
    println!("{:?}", b); 
} 
+0

Ja, ich weiß, wie es mit benutzerdefinierter Deserialisierung zu tun. Wenn ich mehrere Varianten habe, wird dieser Code langwierig, und es passiert mindestens zweimal in meiner aktuellen Aufgabe. Was ich möchte, ist eine Art Annotation im Feld "an_enum", um zu sagen, dass es kein Tag geben wird (oder es bei der Serialisierung weglassen). –

+0

Ich bin nicht vertraut mit der 'r # ... #' Notation. Kannst du es bitte erklären? –

+1

@StevePowell https://stackoverflow.com/questions/26611664/the-operator-r-in-rust –

Verwandte Themen