Serde Rust 解析字符串或结构或结构列表

Serde rust parse string or struct or list of struct

我正在尝试使用 serde

在 rust 中解析以下 JSON
{
    "threads": [
        {
            "md": [
                {
                    "type": "PARAGRAPH",
                    "value": [
                        {
                            "type": "PLAIN_TEXT",
                            "value": "Plain text msg "
                        },
                        {
                            "type": "INLINE_CODE",
                            "value": {
                                "type": "PLAIN_TEXT",
                                "value": "print('hello')"
                            }
                        },
                        {
                            "type": "ITALIC",
                            "value": [
                                {
                                    "type": "PLAIN_TEXT",
                                    "value": "italic text"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

这个代码是:

use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;
use serde::{de, Deserialize, Deserializer};
use serde::de::{MapAccess, SeqAccess, Visitor};
use void::Void;
use std::collections::BTreeMap as Map;

impl FromStr for SubValue {
    type Err = Void;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(SubValue{
            value: s.to_string(),
            value_type: None
        })
    }
}

#[derive(Deserialize, Debug)]
pub struct SubValue {
    value: String,

    #[serde(rename = "type")]
    value_type: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct Value {
    #[serde(rename = "type")]
    value_type: String,
    #[serde(deserialize_with = "string_or_struct")]
    value: SubValue,
}

#[derive(Deserialize, Debug)]
pub struct MessageData {
    #[serde(rename = "type")]
    pub msg_type: String,
    pub value: Vec<Value>,
}

#[derive(Deserialize, Debug)]
pub struct Thread {
    pub md: Vec<MessageData>
}

#[derive(Deserialize, Debug)]
pub struct ThreadList {
    pub threads: Vec<Thread>,
}

fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
    where
        T: Deserialize<'de> + FromStr<Err=Void>,
        D: Deserializer<'de>,
{
    struct StringOrStruct<T>(PhantomData<fn() -> T>);

    impl<'de, T> Visitor<'de> for StringOrStruct<T>
        where
            T: Deserialize<'de> + FromStr<Err=Void>,
    {
        type Value = T;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("string or map or list")
        }

        fn visit_str<E>(self, value: &str) -> Result<T, E>
            where
                E: de::Error,
        {
            Ok(FromStr::from_str(value).unwrap())
        }

        fn visit_seq<M>(self, seq: M) -> Result<T, M::Error>
            where
                M: SeqAccess<'de>,
        {
            Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))
        }

        fn visit_map<M>(self, map: M) -> Result<T, M::Error>
            where M: MapAccess<'de>,
        {
            Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
        }
    }

    deserializer.deserialize_any(StringOrStruct(PhantomData))
}

fn main() {
    let data =
        "{\n\
            \"threads\": [\n\
            {\n\
                \"md\": [\n\
                {\n\
                    \"type\": \"PARAGRAPH\",\n\
                    \"value\": [\n\
                    {\n\
                        \"type\": \"PLAIN_TEXT\",\n\
                        \"value\": \"Plain text msg \"\n\
                    },\n\
                    {\n\
                        \"type\": \"INLINE_CODE\",\n\
                        \"value\": {\n\
                        \"type\": \"PLAIN_TEXT\",\n\
                        \"value\": \"print('hello')\"\n\
                    }\n\
                    },\n\
                    {\n\
                        \"type\": \"ITALIC\",\n\
                        \"value\": [\n\
                        {\n\
                            \"type\": \"PLAIN_TEXT\",\n\
                            \"value\": \"italic text\"\n\
                        }\n\
                        ]\n\
                    }\n\
                    ]\n\
                }\n\
                ]\n\
            }\n\
            ]\n\
        }\n";

   let v: ThreadList = serde_json::from_str(data).expect("Failed to parse");

    for x in v.threads {
        for md in  x.md{
            for val in md.value {
                println!("{}", val.value.value)
            }
        }
    }
}

最大的问题是我无法解析下面的列表 italic。 如果可能的话,我想展平列表并将值结构替换为值“斜体文本”,但它会因 thread 'main' panicked at 'Failed to parse: Error("invalid type: map, expected a string", line: 22, column: 0)', src/main.rs:129:51 而崩溃 API 我正在尝试使用的是火箭聊天获取线程 api https://developer.rocket.chat/reference/api/rest-api/endpoints/team-collaboration-endpoints/chat-endpoints/getthreadslist

反序列化数据的一种方法是使用 enum 来表示不同的值类型及其相关内容:

use serde::{Serialize, Deserialize};

#[derive (Serialize, Deserialize, Debug)]
#[serde (tag = "type", content = "value")]
enum Value {
    #[serde (rename = "PARAGRAPH")]
    Paragraph (Vec<Value>),
    #[serde (rename = "PLAIN_TEXT")]
    PlainText (String),
    #[serde (rename = "INLINE_CODE")]
    InlineCode (Box<Value>),
    #[serde (rename = "ITALIC")]
    Italic (Vec<Value>),
}

Playground