rustc-serialize自定义枚举解码

时间:2016-07-24 00:02:30

标签: json rust

我有一个JSON结构,其中struct的一个字段可以是对象,也可以是数据库中该对象的ID。假设文档看起来像这样两种可能的结构格式:

[
   {
      "name":"pebbles",
      "car":1
   },
   {
      "name":"pebbles",
      "car":{
         "id":1,
         "color":"green"
      }
   }
]

我正在试图找出为此实现自定义解码器的最佳方法。到目前为止,我已经尝试了几种不同的方式,而且我现在卡在这里:

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

以上恐慌与EOL是一个哨兵值rustc-serialize使用它的一些错误枚举。全线是

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

这样做的正确方法是什么?

1 个答案:

答案 0 :(得分:5)

rustc-serialize,或者至少它的JSON解码器,不支持该用例。如果您查看implementation of read_struct_field(或任何其他方法),您可以看到原因:它使用堆栈,但遇到错误时,它不会将堆栈恢复到其原始状态,因此,当您尝试以不同方式解码相同的内容时,解码器将在不一致的堆栈上运行,最终导致意外的EOF值。

我建议你改为Serde。在Serde中反序列化是不同的:不是告诉解码器你期望什么类型,并且没有明确的方法来恢复一个值是错误的类型,Serde调用visitor可以处理任何类型Serde以他们想要的方式支持的类型。这意味着Serde将根据其解析的值的实际类型调用访问者的不同方法。例如,我们可以处理整数以返回OCar::Id和对象以返回OCar::Car

以下是一个完整的例子:

#![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"