Как создать собственный тип для анализа [u8;32] из json, содержащего шестнадцатеричную строку, в Rust

Учитывая следующий JSON:

[
    {
        "dataValueArray": ["0xa4bfa0908dc7b06d98da4309f859023d6947561bc19bc00d77f763dea1a0b9f5"],
        "dataValue": "0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757"
    }
]

Что я пытаюсь проанализировать эти поля json как тип [u8;32] вместо строк.

Мой подход был следующим:


#[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.

Проблема заключается в том, что serde_json::from_str() анализирует значение как строку, но структура назначения ожидает получить тип [u8;32].

Возможно, этот подход совершенно неправильный и мне следует использовать другую стратегию. Есть идеи?

Спасибо

serde не использует вашу FromStr для реализации десериализации, он использует производную Deserialise реализацию, которая ожидает список целых чисел, а не строку в вашем JSON.
cafce25 24.06.2024 14:28
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
1
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Возможным решением является атрибут поля deserialize_with. Вы должны указать пользовательскую функцию для выполнения десериализации:

    #[serde(deserialize_with = "deserialize_json_string")]
    data_value: CommonHash,

И затем реализуйте это:

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)
}

Полный код:

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]);
}

Другие вопросы по теме