将具有多个元素的 JSON 字段从字符串反序列化为 Vec<u8>s 的 Vec

Deserializing a JSON field with multiple elements from Strings to a Vec of Vec<u8>s

我有一个遵循以下示例的 json 结构:

{
    "title": "This is the title of the document",
    "content": "This is a much longer entry containing the full content of a document",
    "version_author": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
    "predecessor": "Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu",
    "co_authors": [
        "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
        "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
        "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
    ]
}

我正在使用 serde_json 将我的 json 文件反序列化为以下 rust 结构:

#[derive(Deserialize)]
struct IpfsConsequence {
    // Specify our own deserializing function to convert JSON string to vector of bytes
    #[serde(deserialize_with = "de_string_to_bytes")]
    title: Vec<u8>,
    #[serde(deserialize_with = "de_string_to_bytes")]
    content: Vec<u8>,
    #[serde(deserialize_with = "de_string_to_bytes")]
    version_author: Vec<u8>,
    #[serde(deserialize_with = "de_string_to_bytes")]
    predecessor: Vec<u8>,
    co_authors: Vec<String>,
}

pub fn de_string_to_bytes<'de, D>(de: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
    let s: &str = Deserialize::deserialize(de)?;
    Ok(s.as_bytes().to_vec())
}

这可以编译,我可以编写代码来完美地使用它。但是使用 co_authors 的 Vec 类型感觉有点乱。我更愿意使用 Vec 类型,但我找不到这样做的方法。

serde_json 很聪明,因为它能够将具有多个值的字段反序列化为 Vec。我希望它在我的 co_authors 字段中继续这样做。但是后来我希望它使用我的“de_string_to_bytes”反序列化器将 co_authors 字段中的每个值转换为 Vecs。

因为我只能将 #[serde(deserialize_with = "de_string_to_bytes")] 宏应用于我的结构中的整个字段,如果我这样做,它将覆盖默认值 serde_json 将具有多个值的字段反序列化为 Vec 的行为,我不想覆盖它。

您可以定义一个类似的函数,将 Vec<&str> 解码为 Vec<Vec<u8>>:

pub fn de_vec_string_to_bytes<'de, D>(de: D) -> Result<Vec<Vec<u8>>, D::Error>
where
    D: Deserializer<'de>,
{
    let v: Vec<&str> = Deserialize::deserialize(de)?;
    Ok(v.into_iter().map(|s| s.as_bytes().to_vec()).collect())
}

Playground

如果你想在更多数据结构中使用它,最好创建一个包装 Vec<u8> 实现 Deserialize 的新类型,然后在任何地方使用该类型:

use std::collections::HashMap;

use serde::{Deserialize, Deserializer};

#[derive(Deserialize)]
struct IpfsConsequence {
    title: Bytes,
    co_authors: Vec<Bytes>,
    maybe_a_map_too: HashMap<String, Bytes>,
}

#[derive(Deserialize)]
struct Bytes(#[serde(deserialize_with = "de_string_to_bytes")] Vec<u8>);

pub fn de_string_to_bytes<'de, D>(de: D) -> Result<Vec<u8>, D::Error>
where
    D: Deserializer<'de>,
{
    let s: &str = Deserialize::deserialize(de)?;
    Ok(s.as_bytes().to_vec())
}

Playground