如何使用Serde进行反序列化时转换字段?
问题描述:
我正在使用Serde反序列化一个XML文件,该文件的十六进制值为0x400
作为字符串,我需要将其转换为值1024
,作为u32
。如何使用Serde进行反序列化时转换字段?
我是否需要实现Visitor
特征,以便将0x分开,然后将400从16位解码到10位?如果是这样,我该怎么做,以便基10整数的反序列化保持不变?
答
最简单的解决方案是使用Serde field attributedeserialize_with
为您的字段设置自定义序列化函数。然后,您可以得到原始字符串和convert it as appropriate:
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use serde::{Deserialize, Deserializer};
use serde::de::Error;
#[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);
}
从这里,这是一个很小的步骤,它推广到自己的类型,以便重新使用:
#[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)
}
}
另请参阅:
Thanks @Shepmaster。这正是我正在寻找的。唯一的问题是对字符串的引用,因为我得到这个恐慌消息:无效类型:字符串“0x400”,预计借用字符串。 – phodina
@phodina我的两个例子都运行成功,显然你已经改变了一些东西。我不是一个心灵读者,所以我不知道那是什么区别。如果XML解码器不能提供字符串切片,您可以尝试'let s:String = ...'。 – Shepmaster
我已经在操场上和我的电脑上试过了你的两个例子,他们都工作。当我将底层格式从json更改为xml时,问题就出现了。这里是代码(操作失误包装箱serde_xml_rs) let raw = r#“ 0xDEADBEEF EtheriumTransaction>”# let transaction:EtheriumTransaction = serde_xml_rs :: deserialize(raw.as_bytes())。unwrap(); –
phodina