如何在Rust中将JSON中的十六进制字符串解析为[u8;32]类型?
问题描述
需要将如下JSON中的dataValueArray和dataValue字段解析为[u8;32]类型:
[ { "dataValueArray": ["0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5"], "dataValue": "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757" } ]
尝试的实现代码如下:
#[derive(Deserialize, Debug)] struct CommonHash([u8;32]); impl FromStr for CommonHash { type Err = std::num::ParseIntError; // Parses a hex string into an instance of CommonHash that it is an alias of [u8;32] fn from_str(hex_str: &str) -> Result<Self, Self::Err> { let mut data: [u8; 32] = [0; 32]; let str_stripped = hex_str.strip_prefix("0x").expect("error stripping the prefix 0x"); hex::decode_to_slice(str_stripped, &mut data).expect("Decoding hex string failed"); Ok(CommonHash(data)) } } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct VectorData { data_value_array: Vec<CommonHash>, data_value: CommonHash, } fn main() { let file_path = "./src/test/vectors/root_vectors.json"; let file_content = fs::read_to_string(file_path).expect("Should have been able to read the file"); let json_file: Vec<VectorData> = serde_json::from_str(&file_content).expect("JSON was not well-formatted"); println!("{:?}", json_file[0].data_value); }
执行后报错:
thread 'main' panicked at 'JSON was not well-formatted: Error("invalid type: string \"0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5\", expected an array of length 32", line: 3, column: 95)', src/main.rs:394:74 stack backtrace: 0: rust_begin_unwind at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/std/src/panicking.rs:593:5 1: core::panicking::panic_fmt at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/panicking.rs:67:14 2: core::result::unwrap_failed at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/result.rs:1651:5 3: core::result::Result<T,E>::expect at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/result.rs:1033:23 4: merkle_tree::main at ./src/main.rs:394:38 5: core::ops::function::FnOnce::call_once at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
问题原因
你的实现思路存在关键遗漏:仅实现FromStr不足以让serde自动将JSON字符串解析为CommonHash类型。serde默认会尝试把JSON字符串映射到结构体的内部类型[u8;32],而[u8;32]在serde的默认行为中期望JSON是一个长度为32的数字数组,而非十六进制字符串,这就是报错的核心原因。
解决方案
需要为CommonHash实现serde的Deserialize trait,或者使用serde的#[serde(deserialize_with)]属性指定自定义解析函数。以下是两种可行方案:
方案1:手动实现Deserialize trait
use serde::{Deserialize, Deserializer}; #[derive(Debug)] struct CommonHash([u8;32]); impl<'de> Deserialize<'de> for CommonHash { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { // 先将JSON字符串解析为String类型 let hex_str = String::deserialize(deserializer)?; let mut data = [0u8; 32]; // 处理0x前缀,转换为serde可识别的错误类型 let stripped = hex_str.strip_prefix("0x") .ok_or_else(|| serde::de::Error::custom("hex string missing 0x prefix"))?; // 解码十六进制字符串到数组,捕获解码错误并转换 hex::decode_to_slice(stripped, &mut data) .map_err(|e| serde::de::Error::custom(format!("failed to decode hex: {}", e)))?; Ok(CommonHash(data)) } } #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct VectorData { data_value_array: Vec<CommonHash>, data_value: CommonHash, } fn main() { let file_path = "./src/test/vectors/root_vectors.json"; let file_content = std::fs::read_to_string(file_path).expect("Should have been able to read the file"); let json_file: Vec<VectorData> = serde_json::from_str(&file_content).expect("JSON was not well-formatted"); println!("{:?}", json_file[0].data_value); }
方案2:使用serde_with库简化实现
如果不想手动编写Deserialize逻辑,可以借助serde_with库的封装能力快速实现:
首先在Cargo.toml添加依赖:
serde_with = "3.4"
然后修改代码:
use serde::{Deserialize, Serialize}; use serde_with::{serde_as, hex::Hex}; #[serde_as] #[derive(Deserialize, Debug)] struct CommonHash(#[serde_as(as = "Hex::with_prefix()")] [u8;32]); #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] struct VectorData { data_value_array: Vec<CommonHash>, data_value: CommonHash, } fn main() { let file_path = "./src/test/vectors/root_vectors.json"; let file_content = std::fs::read_to_string(file_path).expect("Should have been able to read the file"); let json_file: Vec<VectorData> = serde_json::from_str(&file_content).expect("JSON was not well-formatted"); println!("{:?}", json_file[0].data_value); }
Hex::with_prefix()用于支持带0x前缀的十六进制字符串,如果不需要前缀,直接使用Hex即可。
关键说明
- serde默认规则中,固定大小数组
[u8;N]对应JSON的数字数组,而非十六进制字符串,因此必须自定义解析逻辑。 - 手动实现
Deserialize时,需将解析过程中的错误转换为serde的Error类型,避免直接用expect触发panic,保证错误信息更友好。 serde_with库封装了大量常见的序列化/反序列化逻辑,能大幅减少重复代码,提升开发效率。
内容的提问来源于stack exchange,提问作者ARR
相关产品推荐
相关产品推荐

