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>),
}
我正在尝试使用 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>),
}