I've got an interesting use case where I cannot change the structure of the Json data.
Basically, the data has multiple fields with a localised version too, the localised version is just the same field name with _Localised added to it. So for example, the data would look like:
the easiest way to do it is to define the struct according to the data schema. if the struct is also pre-determined, the next step you can do is to define a "shadow" struct or proxy struct, which mimics the data schema, then define a conversion function to convert the "shadow" struct to the real struct.
the problem with this definition is that, the derived Deserialization of LocalisedValue is looking for the fields named value and localised_value, not field1, field2, or localized_field1, localized_field2, etc. even if you manually implements Deserialization for LocalisedValue, you cannot make it looking for field1 sometimes, and field2 other times.
even using serde(flatten), you cannot use the same type LocalisedValue for different fields field1 and field2, they must be distinct types. it's possible, (e.g. with a generic type parameter and manual Deserialization impl), but it's way more complicated than necessary.
I would just use a proxy struct, something like:
// the data type used in code
#[derive(Clone)]
pub struct MyDataType {
field1: LocalisedValue,
field2: LocalisedValue,
}
// the serialized data schema of the data type
#[derive(Clone, Serialize, Deserialize)]
struct MyDataTypeSchema {
field1: String,
field1_Localised: String,
field2: String,
field2_Localised: String,
}
// implement two-way conversion between them
impl From<MyDataType> for MyDataTypeSchema {
//...
}
impl From<MyDataTypeSchema> for MyDataType {
//...
}
// delegate to `MyDataTypeSchema`
impl Serialize for MyDataType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
MyDataTypeSchema::from(self.clone()).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for MyDataType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
MyDataTypeSchema::deserialize(deserializer).map(Into::into)
}
}
most of time, this code can be automatically generated from the schema, e.g. in the build script.