使用 serde Rust 将数据从记录转置到没有中间结构的列
Transpose data from records to columns without intermediate struct using serde Rust
我有两种不同的结构化数据:
1: JSON
{
"key1": 40,
"key2": 50
{,
{
"key1": 41,
"key2": 51
}
2:嵌套数组
[[40,50],[41,51]]
目标是将此数据(我收到的都是 Strings
)反序列化为如下所示的结构:
struct data {
key1: Vec<i8>, // -> [40,41]
key2: Vec<i8> // -> [50,51]
}
我已经有 2 种方法来反序列化每种类型的数据,但问题是对于第一种,我必须创建一个中间 Struct
并将它们收集在 Vec
中然后迭代这个 Vec
将每个元素推到最终 Struct
中的特定 Vec
s。
对于第二个,我反序列化为 Vec<Vec<i8>>
,然后再次迭代逐个元素转置为最终的 Struct
。
我通读了所有 serde
文档并试图找到示例,但无法找到一种方法直接推送到 Struct
的最后 Vec
s 而无需中间件步。
serde
支持吗?如果有,它是如何实现的?
为此,您需要为数组自定义访问者。下面是一个有效的实现。
请注意,虽然我们使用了额外的枚举 InnerData
,但它不需要任何额外的分配,因为该结构仅在堆栈中使用。对于外部数组 InnerData 的每个元素都将被反序列化,并将其字段推送到 Data
结构的字段。
#[serde(untagged)]
允许从平面变体中反序列化枚举(无需在 json 中指定 Map 或 Array)。
也不是,要使用这种类型的反序列化,你需要特别让Deserializer
知道使用哪个Visitor
。如果您的结构是另一个结构的字段之一,您可以使用 #[serde(deserialize_with = ...)]
属性指定它。
use serde::de;
use serde::de::Deserializer;
use serde::Deserialize;
const A: &str = "[{\"key1\": 40, \"key2\": 50}, {\"key1\": 41, \"key2\": 51}]";
const B: &str = "[[40, 50], [41, 51]]";
#[derive(Debug, Deserialize)]
struct Data {
key1: Vec<i8>,
key2: Vec<i8>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum InnerData {
Map { key1: i8, key2: i8 },
Array(i8, i8),
}
struct DataVisitor;
impl<'de> de::Visitor<'de> for DataVisitor {
type Value = Data;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "invalid input")
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut result = Data {
key1: vec![],
key2: vec![],
};
while let Some(inner) = seq.next_element::<InnerData>()? {
let (k1, k2) = match inner {
InnerData::Map { key1, key2 } => (key1, key2),
InnerData::Array(key1, key2) => (key1, key2),
};
result.key1.push(k1);
result.key2.push(k2);
}
Ok(result)
}
}
fn main() {
let mut deserializer = serde_json::Deserializer::from_str(A);
println!("{:?}", deserializer.deserialize_seq(DataVisitor {}));
let mut deserializer = serde_json::Deserializer::from_str(B);
println!("{:?}", deserializer.deserialize_seq(DataVisitor {}));
}
我有两种不同的结构化数据:
1: JSON
{
"key1": 40,
"key2": 50
{,
{
"key1": 41,
"key2": 51
}
2:嵌套数组
[[40,50],[41,51]]
目标是将此数据(我收到的都是 Strings
)反序列化为如下所示的结构:
struct data {
key1: Vec<i8>, // -> [40,41]
key2: Vec<i8> // -> [50,51]
}
我已经有 2 种方法来反序列化每种类型的数据,但问题是对于第一种,我必须创建一个中间 Struct
并将它们收集在 Vec
中然后迭代这个 Vec
将每个元素推到最终 Struct
中的特定 Vec
s。
对于第二个,我反序列化为 Vec<Vec<i8>>
,然后再次迭代逐个元素转置为最终的 Struct
。
我通读了所有 serde
文档并试图找到示例,但无法找到一种方法直接推送到 Struct
的最后 Vec
s 而无需中间件步。
serde
支持吗?如果有,它是如何实现的?
为此,您需要为数组自定义访问者。下面是一个有效的实现。
请注意,虽然我们使用了额外的枚举 InnerData
,但它不需要任何额外的分配,因为该结构仅在堆栈中使用。对于外部数组 InnerData 的每个元素都将被反序列化,并将其字段推送到 Data
结构的字段。
#[serde(untagged)]
允许从平面变体中反序列化枚举(无需在 json 中指定 Map 或 Array)。
也不是,要使用这种类型的反序列化,你需要特别让Deserializer
知道使用哪个Visitor
。如果您的结构是另一个结构的字段之一,您可以使用 #[serde(deserialize_with = ...)]
属性指定它。
use serde::de;
use serde::de::Deserializer;
use serde::Deserialize;
const A: &str = "[{\"key1\": 40, \"key2\": 50}, {\"key1\": 41, \"key2\": 51}]";
const B: &str = "[[40, 50], [41, 51]]";
#[derive(Debug, Deserialize)]
struct Data {
key1: Vec<i8>,
key2: Vec<i8>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum InnerData {
Map { key1: i8, key2: i8 },
Array(i8, i8),
}
struct DataVisitor;
impl<'de> de::Visitor<'de> for DataVisitor {
type Value = Data;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "invalid input")
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut result = Data {
key1: vec![],
key2: vec![],
};
while let Some(inner) = seq.next_element::<InnerData>()? {
let (k1, k2) = match inner {
InnerData::Map { key1, key2 } => (key1, key2),
InnerData::Array(key1, key2) => (key1, key2),
};
result.key1.push(k1);
result.key2.push(k2);
}
Ok(result)
}
}
fn main() {
let mut deserializer = serde_json::Deserializer::from_str(A);
println!("{:?}", deserializer.deserialize_seq(DataVisitor {}));
let mut deserializer = serde_json::Deserializer::from_str(B);
println!("{:?}", deserializer.deserialize_seq(DataVisitor {}));
}