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
相关产品推荐
相关产品推荐

