Я пытаюсь десериализовать поле «имя» в «имя» и «фамилия».
Моя структура:
struct User {
#[serde(rename = "name", deserialize_with = "deserialize_firstname")]
firstname: String,
#[serde(deserialize_with = "deserialize_lastname")]
#[serde(rename = "name")]
lastname: String,
}
И функция десериализации следующая:
fn deserialize_firstname<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: &str = serde::Deserialize::deserialize(deserializer)?;
let re = Regex::new(r"^\W*([\w-]+)").unwrap();
let m = re.find(s);
match m {
Some(value) => Ok( value.as_str().to_string()),
None => Ok("".to_string()),
}
}
(примерно то же самое и с фамилией).
Я получаю следующую ошибку:
err: DeserializeError { field: None, kind: Message("missing field `name`") }
Мой вопрос: как десериализовать поле в два разных поля?
Это то, что я уже сделал, мне было интересно, можно ли это сделать во время десериализации. Чтобы определить пару имя/фамилия, я составил множество правил регулярных выражений, и пока результаты превосходят мои ожидания;)
Я бы предложил создать Name
-заботу, например struct Name
, в которой есть различные компоненты. Затем вы можете реализовать для него собственный сериализатор и десериализатор. Это означает, что нужно использовать такие вещи, как user.name.first
вместо user.first_name
, но я думаю, что ясность, которую вы получите, того стоит.
Serde может сглаживать структуры, делая представление Rust более вложенным, чем сериализованное представление, но он не может разгладить структуры, что вы пытаетесь сделать здесь. Вместо этого вы можете сделать это самостоятельно, аннотировав всю структуру. Здесь у нас есть UserSerde
, который отражает сериализованную структуру, и преобразуем ее в User
, что вам и нужно. (детская площадка)
#[derive(Debug, Deserialize)]
struct UserSerde {
name: Name,
}
#[derive(Debug, Deserialize)]
#[serde(try_from = "&str")]
struct Name {
first: String,
last: String,
}
impl<'a> TryFrom<&'a str> for Name {
type Error = &'static str;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let re = regex::Regex::new(r"^\W*([\w-]+)\W+([\w-]+)\W*$").unwrap();
let Some(caps) = re.captures(value) else {
return Err("could not parse a first and last name");
};
Ok(Self {
first: caps[1].to_string(),
last: caps[2].to_string(),
})
}
}
#[derive(Debug, Deserialize)]
#[serde(from = "UserSerde")]
struct User {
first: String,
last: String,
// other fields...
}
impl From<UserSerde> for User {
fn from(value: UserSerde) -> Self {
let UserSerde {
name: Name { first, last },
} = value;
Self { first, last }
}
}
Обратите внимание: если вы используете это для хранения произвольных человеческих имен, разделять их на имя и фамилию — не очень хорошая идея. По крайней мере, имейте запасной вариант, это нормально String
.
Почему бы не создать свой собственный тип
Name
, который справится с этим? Это также предполагает, что вы можете разумно определить пару имени и фамилии из произвольной строки, в чем я сомневаюсь, поскольку правила здесь чрезвычайно запутаны.