Прошу прощения за несколько неясный вопрос, но я не могу сформулировать его одним предложением. Ниже приведен игрушечный пример, иллюстрирующий проблему:
use serde_json;
use serde_json::Value;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Deserialize, Serialize)]
struct RawInfo {
info: HashMap<u32,String>
}
#[derive(Deserialize, Serialize)]
struct InfoContainer {
info: HashMap<u32,String>
}
#[derive(Deserialize, Serialize)]
struct ContainedInfo {
#[serde(flatten)]
container: InfoContainer
}
#[derive(Deserialize, Serialize)]
struct InfoContainer2 {
info: HashMap<String,String>
}
#[derive(Deserialize, Serialize)]
struct ContainedInfo2 {
#[serde(flatten)]
container: InfoContainer2
}
fn main() {
let jstr = r#"
{
"info": {
"1": "first",
"2": "second",
"3": "third"
}
}
"#;
let jv: Value = match serde_json::from_str(jstr) {
Ok(jv) => jv,
Err(jv) => {
println!("serde json error: {}", jv);
panic!();
}
};
let raw_deserialized = match <RawInfo as Deserialize>::deserialize(jv.clone()) {
Ok(raw_deserialized) => println!("raw is ok"),
Err(raw_deserialized) => println!("raw is error: {}", raw_deserialized)
};
let container_deserialized = match <ContainedInfo as Deserialize>::deserialize(jv.clone()) {
Ok(container_deserialized) => println!("container is ok"),
Err(container_deserialized) => println!("container is error: {}", container_deserialized)
};
let container_deserialized2 = match <ContainedInfo2 as Deserialize>::deserialize(jv.clone()) {
Ok(container_deserialized2) => println!("container2 is ok"),
Err(container_deserialized2) => println!("container2 is error: {}", container_deserialized2)
};
}
Это приводит к следующему результату:
raw is ok
container is error: invalid type: string "1", expected u32
container2 is ok
Таким образом, когда структура Rust имеет ту же структуру, что и JSON, преобразование десериализации ключей работает нормально, но мы пытаемся перенастроить его с помощью flatten
, но это не удается, поскольку целевой HashMap имеет целочисленные ключи, а исходный — строковые.
Ключи JSON должны быть строками, поэтому я не могу это изменить, и мне нужны целочисленные ключи в моем магазине HashMap
. Однако мне бы хотелось еще и сглаживания. Могу ли я добиться этого без необходимости писать собственный десериализатор?
Это очень интересно. У serde_json
есть хак для поддержки десериализации карт с целочисленными ключами, но этот хак не работает с #[serde(flatten)]
, потому что тип не говорит, что ожидает карту.
К счастью, как и во многих других случаях с serde
, вам на помощь приходит serde_with:
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[serde_with::serde_as]
#[derive(Debug, Deserialize, Serialize)]
struct InfoContainer {
#[serde_as(as = "HashMap<serde_with::DisplayFromStr, _>")]
info: HashMap<u32, String>,
}
#[derive(Debug, Deserialize, Serialize)]
struct ContainedInfo {
#[serde(flatten)]
container: InfoContainer,
}
fn main() {
let jstr = r#"
{
"info": {
"1": "first",
"2": "second",
"3": "third"
}
}
"#;
let container_deserialized = serde_json::from_str::<ContainedInfo>(jstr).unwrap();
dbg!(container_deserialized);
}
Отвечает ли это на ваш вопрос? serde_json выравнивает объект с индексами в качестве ключей