How can I serialize with serde_json depending on a state?

Hello guys

I have cryptographic addresses that should be encoded to strings depending on a state. The data is the same, but the encoding prefix is different.

To solve that, imagine some serializer that looks like this:

struct SerializerWithConfig {
    config: Arc<Config>,
    /// ... whatever else is needed
}

then impl serde::Serializer for it... all that is fine.

But then it can never be used, because it's only recognized through the Serializer trait in serde::Serialize:

impl serde::Serialize for Destination {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        // There's no way to access the `config` state here!
    }
}

&self cannot contain that state, unfortunately. That will complicate everything else in the program as it'll ruin many derives.

What's the proper way to solve this problem? Is there a solution for it that doesn't involve setting a global state?

As you already found out, self needs to carry all information. You don't need to implement Serialize on the struct itself, but create a helper struct like

struct DestinationWithConfig<'a> {
  dest: &'a Destination,
  config: &'a Config
}

impl<'a> Serialize for DestinationWithConfig<'a> {...}

Like I mentioned, unfortunately, this isn't an option. Destination (and others in a similar situation) are part of much bigger types, and we can't just pick and choose what to do. The whole grand, grand, grand... parent object, should be serialized, which will eventually lead to serializing Destination.

On the other hand, Config isn't compatible with all the properties of these types, which will result in issues with other derives.

I hope someone replies with a good solution, because I'd love to know, myself. I will just add two points from my own experience:

  1. Use a thread local, not a normal global.
  2. Be aware that some serde codecs rely on [de]serialization being effect-free, and can actually run over the data multiple times. You need to either be able to account for this, or avoid those formats.

Edit: bonus point, you can always implement your own serialization library. It's not a good solution, but can confirm it works wonders when you really need to pass extra context in. :wink:

1 Like

I'm gathering that you haven't even found alternative libraries that solve this problem... this is really unfortunate.

And thanks for the thread-local hint.

Then rethink what you gonna do at all :slight_smile: . For me the requirement is very unusual. Btw. do you also need to implement Deserialize?

In the end you can always do impl Serialize for MyParentParentParentWithConfig { .. } and call ChildWithConfig::new(&self.child, self.config).serialize(serializer)?; So you don't implement serde for your primary structs that don't know how to serialize themselves, but have a whole Serialize and Deserialize bunch of structs that does the job.

I believe you are looking for DeserializeSeed.

1 Like

The OP is trying to conditionally serialize. Maybe GitHub - Marwes/serde_state: Serialization framework for Rust?