使用 serde Rust 将数据从记录转置到没有中间结构的列

Posted

技术标签:

【中文标题】使用 serde Rust 将数据从记录转置到没有中间结构的列【英文标题】:Transpose data from records to columns without intermediate struct using serde Rust 【发布时间】:2021-11-12 18:32:52 【问题描述】:

我有两种不同的数据结构:

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]

我已经有两种方法来反序列化每种类型的数据,但问题是对于第一种方法,我必须创建一个中间 Struct 并将它们收集到 Vec 中,然后迭代这个 Vec在最终的Struct 中将每个元素推送到其特定的Vecs。 对于第二个,我反序列化为Vec&lt;Vec&lt;i8&gt;&gt;,然后再次迭代,逐个元素地转换为最终的Struct

我通读了所有serde 文档并试图找到示例,但找不到直接推送到Struct 的最终Vecs 的方法,而无需中间步骤。 serde 支持这个吗?如果有,它是如何实现的?

【问题讨论】:

【参考方案1】:

为此,您需要为您的数组提供一个自定义访问者。下面是一个有效的实现。

请注意,虽然我们使用了额外的枚举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 ));

小提琴:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f921adbb490970eb9a8b5ef5f9ab49d0

【讨论】:

也许你可以解释一下这些反序列化器会做什么,如何使用它们,或者更好的是提供代码来说明要做什么。 Maxim,你能添加一段代码来演示一下吗? @mbfernan 我对反序列化器实际上是错误的。在摆弄了一下之后,我得出了可行的解决方案。我改了答案,也给代码加了一些解释。

以上是关于使用 serde Rust 将数据从记录转置到没有中间结构的列的主要内容,如果未能解决你的问题,请参考以下文章

SQL 将多行转置到不同的列

rust使用json

将 SQL 行标题转置到第一列

将具有唯一值的列转置到行的 SQL 查询

Rust 和 Serde JSON 反序列化示例?

使用 SORT 进行大型机 JCL 记录转置