ABOUT ME

-

Total
-
  • Rust: serde De/Serialize traits into JSON
    컴퓨터/Rust 2021. 7. 19. 16:55
    728x90
    반응형

    할 것

    A 버튼이 있고, B 버튼이 있을 때 이 두 버튼은 Base 란 trait를 implement 한다.

    (자바로 보면 class A implements Base)

    이 trait를 implement한 struct들을 Vector에 담아서 이 결과를 json으로 바꾸고 싶은데

    하지만 기본적으로는 trait는 불가능하다. 5시간 정도 헤매었다.

     

    erased_serde crate를 사용하면 편해진다.

     

    예제 코드

    공식 문서에 예제가 없어 카카오톡 챗봇 반응에서 버튼 JSON을 만들 때를 코딩했다.

     

    설명

    ButtonJSON이란 struct가 있다.

    이 구조체는 벡터 형식으로 trait를 implement한 모든 구조체들의 모음이다.

     

    Button이란 trait를 보면 new, set_label, set_msg 메소드를 구현하라고 지정했다. (체인 형식)

     

    CallButton, ShareButton, MsgButton 구조체들은 serde에서 Serialize attribute를 derive했다.

     

    맨 아래 kakao_json이란 test 함수를 보면 

    ButtonJSON.buttonsCallButtonShareButton을 저장해서 json string으로 변환해서 출력했다.

    use serde::Serialize;
    
    /***** Buttons *****/
    #[allow(patterns_in_fns_without_body)]
    pub trait Button: Serialize {
        fn new(msg: Option<String>) -> Self;
        fn set_label(mut self, label: String) -> Self;
        fn set_msg(mut self, msg: String) -> Self;
    }
    
    #[derive(Serialize)]
    pub struct ButtonJSON {
        #[serde(skip_serializing_if = "Vec::is_empty")]
        buttons: Vec<Box<dyn erased_serde::Serialize>>,
    }
    
    impl ButtonJSON {
        fn new() -> Self {
            ButtonJSON {
                buttons: Vec::new(),
            }
        }
    }
    
    #[derive(Serialize)]
    #[serde(rename_all = "camelCase")]
    pub struct CallButton {
        label: String,
        action: String,
        phone_number: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        message_text: Option<String>,
    }
    
    impl Button for CallButton {
        fn new(msg: Option<String>) -> Self {
            CallButton {
                label: "label".to_string(),
                action: "phone".to_string(),
                phone_number: "0".to_string(),
                message_text: msg,
            }
        }
    
        fn set_label(mut self, label: String) -> Self {
            self.label = label;
            self
        }
    
        fn set_msg(mut self, msg: String) -> Self {
            self.message_text = Some(msg);
            self
        }
    }
    
    #[derive(Serialize)]
    #[serde(rename_all = "camelCase")]
    pub struct MsgButton {
        label: String,
        action: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        message_text: Option<String>,
    }
    
    impl Button for MsgButton {
        fn new(msg: Option<String>) -> Self {
            MsgButton {
                label: "label".to_string(),
                action: "message".to_string(),
                message_text: msg,
            }
        }
    
        fn set_label(mut self, label: String) -> Self {
            self.label = label;
            self
        }
    
        fn set_msg(mut self, msg: String) -> Self {
            self.message_text = Some(msg);
            self
        }
    }
    
    #[derive(Serialize)]
    #[serde(rename_all = "camelCase")]
    pub struct ShareButton {
        label: String,
        action: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        message_text: Option<String>,
    }
    
    impl Button for ShareButton {
        fn new(msg: Option<String>) -> Self {
            ShareButton {
                label: "label".to_string(),
                action: "share".to_string(),
                message_text: msg,
            }
        }
    
        fn set_label(mut self, label: String) -> Self {
            self.label = label;
            self
        }
    
        fn set_msg(mut self, msg: String) -> Self {
            self.message_text = Some(msg);
            self
        }
    }
    
    /***** Buttons *****/
    
    #[cfg(test)]
    mod test {
        use super::*;
    
        #[test]
        fn kakao_json() {
            let mut result = ButtonJSON::new();
            result.buttons.push(Box::new(
                CallButton::new(None)
                    .set_label("CALL LABEL".to_string())
                    .set_msg("MESSAGE".to_string()),
            ));
            result.buttons.push(Box::new(ShareButton::new(None)));
    
            println!("{}", serde_json::to_string(&result).expect("Woah"));
        }
    }

     

    결과

    {
        "buttons": [
            {
                "label": "CALL LABEL",
                "action": "phone",
                "phoneNumber": "0",
                "messageText": "MESSAGE"
            },
            {
                "label": "label",
                "action": "share"
            }
        ]
    }

     

    참고

    현재 진행 중인 프로젝트

     

    Alfex4936/Rust-Backend

    Rocket web framework을 이용한 Rust 공부. Contribute to Alfex4936/Rust-Backend development by creating an account on GitHub.

    github.com

    728x90

    '컴퓨터 > Rust' 카테고리의 다른 글

    Rust: String 한글 len() (UTF-8)  (0) 2021.07.20
    Rust: diesel db query 코드 정리  (0) 2021.07.18
    Rust: HTML 파싱하기 (crawling)  (0) 2021.07.15

    댓글