You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:42:34