Как настроить реализацию десериализации для определенных типов ввода, но не для всех?
У меня есть такой тип, хотя мой настоящий тип больше и сложнее:
struct MyType {
i: u32,
}
Если я реализую Deserialize
для этого типа serde ищет что-то вроде этого (меня интересует JSON):
{"i":100}
Я хочу настроить его так, чтобы можно было десериализовать и из байтового массива:
[1, 2, 3, 4]
Я могу написать impl для обработки массива, но я хочу, чтобы serde автоматически генерировал остальное (что будет visit_map
):
impl<'de> Deserialize<'de> for MyType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MyTypeVisitor;
impl<'de> Visitor<'de> for MyTypeVisitor {
type Value = MyType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "struct or array of 4 integers")
}
fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
// ...
}
}
// deserializer.deserialize_any(MyTypeVisitor)
}
}
Это возможно? В этом примере это не сложно, но когда структура большая, десериализация может быть болезненной.
Это не дубликат Как преобразовать поля при десериализации с использованием Serde? так как deserialize_with
работает только на 1 поле. Я не могу понять, как я мог бы заставить это работать для моего реального типа:
pub enum Component {
String(StringComponent),
Translation(TranslationComponent),
Score(ScoreComponent),
Selector(SelectorComponent),
}
pub struct StringComponent {
#[serde(flatten)] pub base: Base,
pub text: String,
}
pub struct Base {
// ...
extra: Option<Vec<Component>>,
// ...
}
Что я хочу сделать, это:
- При десериализации, если вход является числом, вернуть
Component::String
, Это может быть сделано сvisit_i
/u
/f64
и друзья. - Если вход является строкой, вернуть
Component::String
снова. Это может быть сделано сvisit_str
/string
, - Если вход является массивом
[..]
, десериализовать его как обычно, но сделать присвоение элементов в массиве [1..] дополнительному массиву [0]. Это может быть сделаноvisit_seq
, - Если вход является картой, пусть serde производный обрабатывает его.
1 ответ
В документации Serde есть пример, показывающий, как реализовать десериализацию из строки или структуры. Это эквивалентно вашему случаю, только меньше.
Важная часть заключается в следующем:
fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
where
M: MapAccess<'de>,
{
Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))
}
Это делегирует встроенную реализацию десериализации. Так как все остальные ваши дела являются заказными, это должно подойти.