Given the next json:
[
{
"dataValueArray": ["0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5"],
"dataValue": "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757"
}
]
What i'm trying to parse those fields of the json as type [u8;32] instead of strings.
My approach has been the next one:
#[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);
}
The exit of the execution is:
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.
The problem seems that serde_json::from_str() is parsing the value as string but the destination struct expects to receive the type [u8;32].
Maybe this approach is completely wrong and I should use another strategy. Any idea?
Thank you
A possible solution is the deserialize_with
field attribute. You must specify a custom function to perform deserialization:
#[serde(deserialize_with = "deserialize_json_string")]
data_value: CommonHash,
And then implement it:
fn deserialize_json_string<'de, D: de::Deserializer<'de>>(
deserializer: D,
) -> Result<CommonHash, D::Error> {
let s: &str = de::Deserialize::deserialize(deserializer)?;
CommonHash::from_str(s).map_err(de::Error::custom)
}
Full code:
use anyhow::{Context, Result};
use serde::{de, Deserialize};
#[derive(Deserialize, Debug)]
struct CommonHash([u8; 32]);
impl CommonHash {
fn from_str(hex_str: &str) -> Result<Self> {
let mut data: [u8; 32] = [0; 32];
let str_stripped = hex_str
.strip_prefix("0x")
.context("error stripping the prefix 0x")?;
hex::decode_to_slice(str_stripped, &mut data)?;
Ok(CommonHash(data))
}
}
fn deserialize_json_string<'de, D: de::Deserializer<'de>>(
deserializer: D,
) -> Result<CommonHash, D::Error> {
let s: &str = de::Deserialize::deserialize(deserializer)?;
CommonHash::from_str(s).map_err(de::Error::custom)
}
fn deserialize_json_list<'de, D: de::Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<CommonHash>, D::Error> {
let arr: Vec<&str> = de::Deserialize::deserialize(deserializer)?;
arr.into_iter()
.map(|s| CommonHash::from_str(&s))
.collect::<Result<Vec<CommonHash>>>()
.map_err(de::Error::custom)
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct VectorData {
#[serde(deserialize_with = "deserialize_json_list")]
data_value_array: Vec<CommonHash>,
#[serde(deserialize_with = "deserialize_json_string")]
data_value: CommonHash,
}
fn main() {
let file_content = r#"[
{
"dataValueArray": ["0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5"],
"dataValue": "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757"
}
]"#;
let json_file: Vec<VectorData> =
serde_json::from_str(&file_content).expect("JSON was not well-formatted");
println!("{:?}", json_file[0]);
}