如何为盒装特征对象实现`serde :: Serialize`?

时间:2018-04-25 12:05:44

标签: rust polymorphism serde

我遇到了一个试图为结构创建通用向量的问题。 这是我的第一次尝试:

#[derive(Serialize)]
struct Card {
    sections: Vec<Section<WidgetTrait>>
}

#[derive(Serialize)]
struct Section<T: WidgetTrait> {
    header: String,
    widgets: Vec<T>
}

这导致我发现Sized未实现且编译时未知WidgetTrait大小的错误。

我的下一次尝试是使用Box<WidgetTrait>,如此:

#[derive(Serialize)]
struct Section {
    header: String,
    widgets: Vec<Box<WidgetTrait>>
}

Playground

这导致我出错:

error[E0277]: the trait bound `WidgetTrait: serde::Serialize` is not satisfied
  --> src/main.rs:11:10
   |
11 | #[derive(Serialize)]
   |          ^^^^^^^^^ the trait `serde::Serialize` is not implemented for `WidgetTrait`
   |
   = note: required because of the requirements on the impl of `serde::Serialize` for `std::boxed::Box<WidgetTrait>`
   = note: required because of the requirements on the impl of `serde::Serialize` for `std::vec::Vec<std::boxed::Box<WidgetTrait>>`
   = note: required by `serde::ser::SerializeStruct::serialize_field`

我的目标是让Section struct中的小部件向量能够接受实现WidgetTrait特征的不同类型的小部件,就像使用接口一样。

2 个答案:

答案 0 :(得分:5)

要序列化Serde trait对象,您应该使用erased-serde

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate erased_serde;

extern crate serde;
extern crate serde_json;

#[derive(Serialize)]
struct Card {
    sections: Vec<Section>,
}

#[derive(Serialize)]
struct Section {
    header: String,
    widgets: Vec<Box<WidgetTrait>>,
}

#[derive(Serialize)]
struct Image {
    image_url: String,
}

#[derive(Serialize)]
struct KeyValue {
    top_label: String,
    content: String,
}

trait WidgetTrait: erased_serde::Serialize {}
impl WidgetTrait for Image {}
impl WidgetTrait for KeyValue {}

serialize_trait_object!(WidgetTrait);

fn main() {
    let card = Card {
        sections: vec![
            Section {
                header: "text".to_owned(),
                widgets: vec![
                    Box::new(Image {
                        image_url: "img".to_owned(),
                    }),
                    Box::new(KeyValue {
                        top_label: "text".to_owned(),
                        content: "text".to_owned(),
                    }),
                ],
            },
        ],
    };

    println!("{}", serde_json::to_string_pretty(&card).unwrap());
}

答案 1 :(得分:1)

我解决了编译错误:

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

use serde::ser::{Serialize, Serializer, SerializeStruct};

#[derive(Serialize)]
struct Card {
    sections: Vec<Section>
}

#[derive(Serialize)]
struct Section {
    header: String,
    widgets: Vec<Box<WidgetTrait>>
}

#[derive(Serialize)]
struct Image {
    #[serde(rename = "imageUrl")]
    image_url: String
}

#[derive(Serialize)]
struct KeyValue {
    #[serde(rename = "topLabel")]
    top_label: String,

    content: String
}

trait WidgetTrait {}

impl Serialize for WidgetTrait {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer {
            let s = serializer.serialize_struct("???", 3)?;

            s.end()
        }
}

impl WidgetTrait for Image {}
impl WidgetTrait for KeyValue {}

fn main() {
    // let test = ResponseMessage { 
    //         text: None, 
    //         cards: Some(
    //             vec![Card { sections: vec![
    //                 Section { header: format!("text"), widgets: vec![ 
    //                     Box::new(Image { image_url: format!("img") }) 
    //                     ]},
    //                 Section { header: format!("text"), widgets: vec![
    //                      Box::new(KeyValue { top_label: format!("text"), content: format!("text") }),
    //                      Box::new(KeyValue { top_label: format!("text"), content: format!("text") })
    //                      ]}
    //                 ]}])
    //         }
}

Playground

工作解决方案的步骤。

  1. 根据How to get a struct reference from a boxed trait?编写实施as_any()的结构的WidgetTrait实现。
  2. Serialize
  3. 类型的特征Box<WidgetTrait>添加实施
  4. 向结构转发Box<Widget>,以便我们知道使用as_any()downcast_ref()
  5. 的类型
  6. 使用documentation了解如何序列化强类型结构
  7. #[macro_use]
    extern crate serde_derive;
    extern crate serde_json;
    extern crate serde;
    
    use serde::ser::{Serialize, Serializer, SerializeStruct};
    use std::any::Any;
    
    #[derive(Serialize)]
    struct Card {
        sections: Vec<Section>
    }
    
    #[derive(Serialize)]
    struct Section {
        header: String,
        widgets: Vec<Box<WidgetTrait>>
    }
    
    #[derive(Serialize)]
    struct Image {
        #[serde(rename = "imageUrl")]
        image_url: String
    }
    
    #[derive(Serialize)]
    struct KeyValue {
        #[serde(rename = "topLabel")]
        top_label: String,
    
        content: String
    }
    
    trait WidgetTrait {
        fn as_any(&self) -> &Any;
    }
    
    impl Serialize for Box<WidgetTrait> {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 
            where S: Serializer {
                return match self.as_any().downcast_ref::<Image>() {
                    Some(img) => {
                            let mut widget_serializer = serializer.serialize_struct("Image", 1)?;
                            widget_serializer.serialize_field("imageUrl", &img.image_url)?;
    
                            widget_serializer.end()  
                        },
                    None => {
                        let key_value: &KeyValue = match self.as_any().downcast_ref::<KeyValue>() {
                            Some(k) => k,
                            None => panic!("Unknown type!")
                        };
    
                        let mut widget_serializer = serializer.serialize_struct("KeyValue", 2)?;
                        widget_serializer.serialize_field("topLabel", &key_value.top_label)?;
                        widget_serializer.serialize_field("content", &key_value.content)?;
    
                        widget_serializer.end()  
                    }
                };                
            }
    }
    
    impl WidgetTrait for Image {
        fn as_any(&self) -> &Any {
            self
        }
    }
    
    impl WidgetTrait for KeyValue {
        fn as_any(&self) -> &Any {
            self
        }
    }
    
    fn main() {
        // let test = ResponseMessage { 
        //         text: None, 
        //         cards: Some(
        //             vec![Card { sections: vec![
        //                 Section { header: format!("text"), widgets: vec![ 
        //                     Box::new(Image { image_url: format!("img") }) 
        //                     ]},
        //                 Section { header: format!("text"), widgets: vec![
        //                      Box::new(KeyValue { top_label: format!("text"), content: format!("text") }),
        //                      Box::new(KeyValue { top_label: format!("text"), content: format!("text") })
        //                      ]}
        //                 ]}])
        //         }
    }
    

    Playground