You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust使用serde解析Rocket Chat线程API嵌套JSON报错求助

问题原因

你当前的报错是因为string_or_struct反序列化数组时,直接尝试将整个数组反序列化为SubValue结构体,但SubValue默认只能匹配字符串或者带value字段的对象,无法兼容数组结构。

解决方案

我们调整自定义反序列化逻辑,遇到数组时先将其解析为Vec<Value>,递归提取所有子元素的文本内容拼接后再构造SubValue,就能实现自动展平的效果。

修改后的完整代码

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;

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, Clone)]
pub struct SubValue {
    pub value: String,

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

#[derive(Deserialize, Debug, Clone)]
pub struct Value {
    #[serde(rename = "type")]
    pub value_type: String,
    #[serde(deserialize_with = "string_or_struct")]
    pub 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, D>(deserializer: D) -> Result<SubValue, D::Error>
    where
        D: Deserializer<'de>,
{
    struct StringOrStruct(PhantomData<fn() -> SubValue>);

    impl<'de> Visitor<'de> for StringOrStruct
    {
        type Value = SubValue;

        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<SubValue, E>
            where
                E: de::Error,
        {
            Ok(FromStr::from_str(value).unwrap())
        }

        fn visit_seq<M>(self, mut seq: M) -> Result<SubValue, M::Error>
            where
                M: SeqAccess<'de>,
        {
            let mut text = String::new();
            // 遍历数组中所有Value元素,拼接文本
            while let Some(val) = seq.next_element::<Value>()? {
                text.push_str(&val.value.value);
            }
            Ok(SubValue {
                value: text,
                value_type: None,
            })
        }

        fn visit_map<M>(self, map: M) -> Result<SubValue, M::Error>
            where M: MapAccess<'de>,
        {
            SubValue::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)
            }
        }
    }
}

核心改动点

  • 把泛型的string_or_struct改成专门针对SubValue的反序列化函数,避免泛型匹配混乱
  • 重写visit_seq方法:遍历数组中的所有Value元素,递归提取每个元素的文本内容拼接成一个完整字符串,再构造SubValue返回
  • 给Value和SubValue加Clone派生,满足遍历拼接时的取值要求

运行修改后的代码会正常输出:

Plain text msg 
print('hello')
italic text

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 17:06:05