2016-07-24 18 views
4

構造体のフィールドの1つがオブジェクトまたはデータベース内のそのオブジェクトのIDのいずれかになるJSON構造を持っています。文書は、構造体の両方の可能なフォーマットで、次のようになりましょう:私は、このためのカスタムデコーダを実装するための最良の方法を把握しようとしているrustc-serializeカスタムenumデコード

​​

。これまでのところ、私はいくつかの異なる方法を試してみた、と私は現在、ここにこだわっ:

extern crate rustc_serialize; 

use rustc_serialize::{Decodable, Decoder, json}; 

#[derive(RustcDecodable, Debug)] 
struct Car { 
    id: u64, 
    color: String 
} 

#[derive(Debug)] 
enum OCar { 
    Id(u64), 
    Car(Car) 
} 

#[derive(Debug)] 
struct Person { 
    name: String, 
    car: OCar 
} 

impl Decodable for Person { 
    fn decode<D: Decoder>(d: &mut D) -> Result<Person, D::Error> { 
    d.read_struct("root", 2, |d| { 
     let mut car: OCar; 

     // What magic must be done here to get the right OCar? 

     /* I tried something akin to this: 
     let car = try!(d.read_struct_field("car", 0, |r| { 
     let r1 = Car::decode(r); 
     let r2 = u64::decode(r); 

     // Compare both R1 and R2, but return code for Err() was tricky 
     })); 
     */ 

     /* And this got me furthest */ 
     match d.read_struct_field("car", 0, u64::decode) { 
     Ok(x) => { 
      car = OCar::Id(x); 
     }, 
     Err(_) => { 
      car = OCar::Car(try!(d.read_struct_field("car", 0, Car::decode))); 
     } 
     } 


     Ok(Person { 
     name: try!(d.read_struct_field("name", 0, Decodable::decode)), 
     car: car 
     }) 
    }) 
    } 
} 

fn main() { 
    // Vector of both forms 
    let input = "[{\"name\":\"pebbles\",\"car\":1},{\"name\":\"pebbles\",\"car\":{\"id\":1,\"color\":\"green\"}}]"; 

    let output: Vec<Person> = json::decode(&input).unwrap(); 

    println!("Debug: {:?}", output); 
} 

上記のパニックをセンチネル値rustcシリアル化は、そのエラー列挙型のいくつかに使用しているEOLと。フルラインは

thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: EOF', src/libcore/result.rs:785 

これは正しい方法はありますか?

答えて

5

rustc-serialize、または少なくともJSONデコーダは、その使用例をサポートしていません。 implementation of read_struct_field(または他の方法)を見ると、なぜスタックが使用されているのかがわかりますが、エラーが発生したときにスタックを元の状態に復元することはありません。したがって、デコードしようとすると別の点では、デコーダは一貫性のないスタック上で動作しており、最終的に予期しないEOF値につながります。

代わりにSerdeを調べてみることをおすすめします。 Serdeのデシリアライズは異なります。デコーダがどのタイプを予期しているのか、値が間違ったタイプの場合に回復する明確な方法がない場合、SerdeはSerdeがサポートするタイプのいずれかを処理できるvisitorを呼び出しますそれが望む方法。これは、Serdeが、解析した値の実際の型に応じて、訪問者に対して異なるメソッドを呼び出すことを意味します。たとえば、OCar::Idを返す整数と​​3210を返すオブジェクトを扱うことができます。

#![feature(custom_derive, plugin)] 
#![plugin(serde_macros)] 

extern crate serde; 
extern crate serde_json; 

use serde::de::{Deserialize, Deserializer, Error, MapVisitor, Visitor}; 
use serde::de::value::MapVisitorDeserializer; 

#[derive(Deserialize, Debug)] 
struct Car { 
    id: u64, 
    color: String 
} 

#[derive(Debug)] 
enum OCar { 
    Id(u64), 
    Car(Car), 
} 

struct OCarVisitor; 

#[derive(Deserialize, Debug)] 
struct Person { 
    name: String, 
    car: OCar, 
} 

impl Deserialize for OCar { 
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer { 
     deserializer.deserialize(OCarVisitor) 
    } 
} 

impl Visitor for OCarVisitor { 
    type Value = OCar; 

    fn visit_u64<E>(&mut self, v: u64) -> Result<Self::Value, E> where E: Error { 
     Ok(OCar::Id(v)) 
    } 

    fn visit_map<V>(&mut self, visitor: V) -> Result<Self::Value, V::Error> where V: MapVisitor { 
     Ok(OCar::Car(try!(Car::deserialize(&mut MapVisitorDeserializer::new(visitor))))) 
    } 
} 

fn main() { 
    // Vector of both forms 
    let input = "[{\"name\":\"pebbles\",\"car\":1},{\"name\":\"pebbles\",\"car\":{\"id\":1,\"color\":\"green\"}}]"; 

    let output: Vec<Person> = serde_json::from_str(input).unwrap(); 

    println!("Debug: {:?}", output); 
} 

出力:

Debug: [Person { name: "pebbles", car: Id(1) }, Person { name: "pebbles", car: Car(Car { id: 1, color: "green" }) }] 

Cargo.toml:

[dependencies] 
serde = "0.7" 
serde_json = "0.7" 
serde_macros = "0.7" 
+0

非常に良い答え

はここで完全な例です!私は掘り出しながらスタックに気づいたが、データを何回も引き出す​​か、エラーを投げることがそれに影響を与えるかを確認する時間を費やさなかった。しかし1つの問題:これはserde_macrosのため夜間モードになります。その周りに道がありますか? – RandomInsano

+2

[安定した錆とserde_codegenでSerdeを使う](https://github.com/serde-rs/serde#using-serde-with-stable-rust-and-serde_codegen)を参照してください。 –

関連する問題