A simplest way would be to have two structs: one with #[derive(Serialize, Deserialize)] on it, and another with something like TryFrom/Into targeting the first one. This way, you'll decouple storage format (handled by serde) from your invariants (handled by TryFrom).