将盒装特征的矢量序列化为JSON

时间:2015-11-12 02:23:16

标签: json serialization rust

我正在尝试序列化struct的集合,这些集合可能是不同类型但实现特征。以下内容未在围栏中编译,但显示了我要做的事情:

extern crate rustc_serialize;

trait Account {
    fn get_name(&self) -> &String;
}

#[derive(RustcEncodable, RustcDecodable)]
struct Account1 {
    name: String,
}

impl Account1 {
    fn new() -> Account1 {
        Account1 { name: String::from("Account1") }
    }
}

impl Account for Account1 {
    fn get_name(&self) -> &String {
        &self.name
    }
}

#[derive(RustcEncodable, RustcDecodable)]
struct Account2 {
    name: String,
}

impl Account2 {
    fn new() -> Account2 {
        Account2 { name: String::from("Account2") }
    }
}

impl Account for Account2 {
    fn get_name(&self) -> &String {
        &self.name
    }
}

#[derive(RustcEncodable, RustcDecodable)]
struct Accounts {
    accounts: Vec<Box<Account>>
}

impl Accounts {
    fn new() -> Accounts {
        let accs: Vec<Box<Account>> = Vec::new();
        Accounts { accounts: accs }
    }

    fn add_account(&mut self, account: Box<Account>) {
        self.accounts.push(account);
    }
}

fn main() {
    let mut accounts = Accounts::new();
    let acc1 = Box::new(Account1::new());
    accounts.add_account(acc1);
    let acc2 = Box::new(Account2::new());
    accounts.add_account(acc2);
}

Playpen

在我的机器上,我收到以下错误:

error: the trait `core::marker::Sized` is not implemented for the type `account::Account` [E0277]

我认为错误是说要使其工作,编译器需要在编译时知道每个Box -ed元素的大小。我该如何解决这个问题?

我认为我这样做是如何在Java / C ++中实现的,但我应该以不同的方式思考Rust。请建议是否有更好的替代方案来实现这一目标。

2 个答案:

答案 0 :(得分:2)

我建议您远离struct s +普通trait设置并使用enum。这显然不允许您的包的用户添加新的帐户类型,但即使以某种方式允许,您的反序列化代码也永远不会知道新类型,也无法反序列化它们。肯定有办法注册&#34;某些反序列化器取决于某些标签,但只要您有一组固定的帐户类型,枚举就允许您使用现有的(反)序列化框架。

首先,您创建一个通用Account类型,其中包含帐户常用的所有字段

#[derive(RustcEncodable, RustcDecodable)]
struct Account {
    name: String,
    kind: AccountType,
}

然后你创建一个枚举,其中包含所有accound类型的变体。如果帐户类型需要额外的字段,只需将它们添加到相应的枚举变体。

#[derive(RustcEncodable, RustcDecodable)]
enum AccountType {
    Account1,
    Account2,
}

您的Accounts经理甚至不再需要Box es:

#[derive(RustcEncodable, RustcDecodable)]
struct Accounts {
    accounts: Vec<Account>
}

您可以尝试使用Playground中的所有内容,但请确保使用rustc_serialize而不是我在Playground中使用的黑客,以使其在操场上运行。除前两行之外的所有行都是100%兼容的。

答案 1 :(得分:1)

EncodableDecodable特征的自动生成实现不会做正确的事情;我不确定我以前见过某人尝试序列化特征对象。没有struct反思知道要为特征输出什么名称和类型。

要解决您的问题,您可以自己实施这些特征。这是一个只创建名称数组的示例:

impl Encodable for Accounts {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_seq(self.accounts.len(), |s| {
            for (idx, a) in self.accounts.iter().enumerate() {
                try!(s.emit_seq_elt(idx, |s| {
                    s.emit_str(a.get_name())
                }))
            }

            Ok(())
        })
    }
}

但是,我不知道你会怎么写一个解码器 - 你会选择哪种具体类型来实现这个特性?

供参考,以下是通过--pretty expanded rustc选项找到的自动生成的实现。为了便于阅读,我清理了一下:

impl Decodable for Accounts {
    fn decode<D: Decoder>(arg: &mut D) -> Result<Accounts, D::Error> {
        arg.read_struct("Accounts", 1, |d| {
            let a = try!(d.read_struct_field("accounts", 0usize, Decodable::decode));
            Ok(Accounts { accounts: a })
        })
    }
}

编译错误:

src/main.rs:118:66: 118:83 error: the trait `core::marker::Sized` is not implemented for the type `Account` [E0277]
src/main.rs:118             let a = try!(d.read_struct_field("accounts", 0usize, Decodable::decode));
                                                                                 ^~~~~~~~~~~~~~~~~