/ / ¿Cómo personalizo una implementación Deserialize para ciertos tipos de entrada pero no todos? - óxido, serde, serde-json

¿Cómo personalizo una implementación Deserialize para ciertos tipos de entrada pero no todos? - óxido, serde, serde-json

Tengo un tipo como este, aunque mi tipo real es más grande y más complejo:

struct MyType {
i: u32,
}

Si implemento Deserialize para este tipo, serde busca algo como esto (estoy interesado en JSON):

{"i":100}

Quiero personalizarlo para poder deserializar desde una matriz de bytes también:

[1, 2, 3, 4]

Puedo escribir un impl para manejar la matriz, pero quiero que serde genere automáticamente el resto (que será 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)
}
}

¿Es eso posible? En este ejemplo, no es difícil, pero cuando la estructura es grande, la deserialización a mano puede ser dolorosa.

Esto no es un duplicado de ¿Cómo transformar campos durante la deserialización utilizando Serde? porque deserialize_with Funciona solo para 1 campo. No puedo entender cómo lo haría funcionar para mi tipo real:

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>>,
// ...
}

Lo que quiero hacer es:

  • Durante la deserialización, si la entrada es un número, devuelva un Component::String. Esto se puede hacer con visit_i/u/f64 y amigos.
  • Si la entrada es una cadena, devuelve un Component::String otra vez. Esto se puede hacer con visit_str/string.
  • Si la entrada es una matriz [..], deserialícelo como de costumbre, pero asigne elementos en la matriz [1 ..] a extra de la matriz [0]. Esto se puede hacer por visit_seq.
  • Si la entrada es un mapa, deje que serde derive lo maneje.

Respuestas

1 para la respuesta № 1

los Serde documentacion tiene un ejemplo que muestra cómo implementar deserialización desde ya sea una cadena o una estructura. Esto es equivalente a tu caso, solo que más pequeño.

La parte importante es esta:

fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
where
M: MapAccess<"de>,
{
Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))
}

Esto delega a la implementación de deserialización incorporada. Ya que todos sus otros casos son personalizados, esto debería ser adecuado.