【发布时间】:2018-03-27 00:12:58
【问题描述】:
我正在使用 Serde 反序列化具有十六进制值 0x400 作为字符串的 XML 文件,我需要将其转换为值 1024 作为 u32。
我是否需要实现 Visitor 特征,以便分隔 0x,然后将 400 从基数 16 解码到基数 10?如果是这样,我该怎么做才能使以 10 为底的整数的反序列化保持不变?
【问题讨论】:
我正在使用 Serde 反序列化具有十六进制值 0x400 作为字符串的 XML 文件,我需要将其转换为值 1024 作为 u32。
我是否需要实现 Visitor 特征,以便分隔 0x,然后将 400 从基数 16 解码到基数 10?如果是这样,我该怎么做才能使以 10 为底的整数的反序列化保持不变?
【问题讨论】:
deserialize_with 属性最简单的解决方案是使用Serde field attributedeserialize_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);
}
注意这如何使用任何其他现有的 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)
}
}
此方法还允许您添加或删除字段,因为“内部”反序列化类型基本上可以为所欲为。
from 和 try_from 属性您还可以将上面的自定义转换逻辑放入From 或TryFrom 实现中,然后通过from 或try_from 属性指示Serde 使用它:
#[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)
}
}
另见:
【讨论】:
let s: String = ...。
let s: String as I suggested earlier 有效。