Search code examples
rustserde

Using serde for two (de)serialization formats


I have successfully used serde_json to deserialize and serialize JSON. My setup looks somewhat like this (very simplified):

use serde::{Deserialize, Serialize};
use serde_json;
use serde_with::skip_serializing_none;

#[skip_serializing_none]
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct Foo {
    #[serde(flatten)]
    bar: Option<Bar>,
    
    baz_quux: Option<u8>,
}

#[skip_serializing_none]
#[derive(Deserialize, Serialize)]
struct Bar {
    #[serde(rename = "plughXyzzySomeRandomStuff")]
    plugh_xyzzy: Option<u8>
}

And then I have implemented FromStr and Display on Foo, which in turn call serde_json::from_str and serde_json::to_string respectively, to easily (de)serialize the struct.

However, I'd now like to also use serde_ini to support (de)serializing INI files as well, to the same Rust data structure. But I can't really figure out how to do that.

The structure itself is simple enough, but my specific problems are with the attributes:

  • The keys are named differently in the JSON and INI formats (the JSON format uses the customary camelCase, while the INI doesn't), so I have to solve the #[serde(rename)] and #[serde(rename_all)] attributes some other way, but I'm not sure where or how.
  • #[serde(flatten)] doesn't seem to work with serde_ini's all-string values, which require a #[serde(deserialize_with="from_str)]" attribute for all non-string values, but this should obviously only apply to the INI values and not the JSON ones.

So all in all, I guess what I need to do is to re-implement these attributes, or use them conditionally based on what (De)Serializer is used, but I'm stumped on how to do that.


Solution

  • This is a limitation of serde's design. The Deserialize and Serialize implementations are intentionally separated from the Serializer and Deserializer implementations, which gives great flexibility and convenience when choosing different formats and swapping them out. Unfortunately, it means it is isn't possible to individually fine-tune your Deserialize and Serialize implementations for different formats.

    The way I have done this before is to duplicate the data types so that I can configure them for each format, and then provide a zero-cost conversion between them.