rust - 如何使用 Serde 在反序列化期间转换字段?

标签 rust hex serde

我正在使用 Serde 反序列化一个 XML 文件,该文件的十六进制值 0x400 是一个字符串,我需要将它转换为值 1024 作为一个 u32.

我是否需要实现 Visitor 特性,以便将 0x 分开,然后将 400 从 16 进制解码到 10 进制?如果是这样,我该怎么做才能使以 10 为基数的整数的反序列化保持不变?

最佳答案

deserialize_with 属性

最简单的解决方案是使用 Serde field attribute deserialize_with为您的字段设置自定义序列化函数。然后您可以获得原始字符串和 convert it as appropriate :

use serde::{de::Error, Deserialize, Deserializer}; // 1.0.94
use serde_json; // 1.0.40

#[derive(Debug, Deserialize)]
struct EtheriumTransaction {
    #[serde(deserialize_with = "from_hex")]
    account: u64, // hex
    amount: u64, // decimal
}

fn from_hex<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
    D: Deserializer<'de>,
{
    let s: &str = Deserialize::deserialize(deserializer)?;
    // do better hex decoding than this
    u64::from_str_radix(&s[2..], 16).map_err(D::Error::custom)
}

fn main() {
    let raw = r#"{"account": "0xDEADBEEF", "amount": 100}"#;
    let transaction: EtheriumTransaction =
        serde_json::from_str(raw).expect("Couldn't derserialize");
    assert_eq!(transaction.amount, 100);
    assert_eq!(transaction.account, 0xDEAD_BEEF);
}

playground

注意这如何使用任何其他现有的 Serde 实现来解码。在这里,我们解码为一个字符串切片(let s: &str = Deserialize::deserialize(deserializer)?)。您还可以创建直接映射到原始数据的中间结构,在它们上派生 Deserialize,然后在您的 Deserialize 实现中对它们进行反序列化。

实现serde::Deserialize

从这里开始,这是将它提升为您自己的类型以允许重用它的一小步:

#[derive(Debug, Deserialize)]
struct EtheriumTransaction {
    account: Account, // hex
    amount: u64,      // decimal
}

#[derive(Debug, PartialEq)]
struct Account(u64);

impl<'de> Deserialize<'de> for Account {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s: &str = Deserialize::deserialize(deserializer)?;
        // do better hex decoding than this
        u64::from_str_radix(&s[2..], 16)
            .map(Account)
            .map_err(D::Error::custom)
    }
}

playground

此方法还允许您添加或删除字段,因为“内部”反序列化类型基本上可以做任何它想做的事情。

fromtry_from 属性

您还可以将上面的自定义转换逻辑放入 FromTryFrom 实现中,然后指示 Serde 通过 from 使用它或 try_from属性:

#[derive(Debug, Deserialize)]
struct EtheriumTransaction {
    account: Account, // hex
    amount: u64,      // decimal
}

#[derive(Debug, PartialEq, Deserialize)]
#[serde(try_from = "IntermediateAccount")]
struct Account(u64);

#[derive(Deserialize)]
struct IntermediateAccount<'a>(&'a str);

impl<'a> TryFrom<IntermediateAccount<'a>> for Account {
    type Error = std::num::ParseIntError;

    fn try_from(other: IntermediateAccount<'a>) -> Result<Self, Self::Error> {
        // do better hex decoding than this
        u64::from_str_radix(&other.0[2..], 16).map(Self)
    }
}

另见:

关于rust - 如何使用 Serde 在反序列化期间转换字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54558741/

相关文章:

rust - 为什么可变借用的顺序在 Rust 中很重要?

vector - 如何将字符串的 HashSet 转换为向量?

rust - 来自原始 fd 的 BufReader

c++ - 如何将十六进制存储在数组中? C++ 多线程

json - 如何将Serde与具有不同对象的JSON数组一起使用,以获取成功和错误?

rust - * mut(dyn std::ops::Fn()+ 'static)`无法使用serde::de::DeserializeOwned结构在线程之间安全共享

rust - rust-具有特征的泛型函数的返回类型

c# - 将十六进制字符串转换回字符

c++ - 如何打印 unsigned long long 的所有可能的唯一位掩码?

rust - 函数局部变量的生命周期不够长