Decorated Enums — another way of serialising Enums in Dart
Luke Urban has written a really good article on ways of serialising enums in Dart, and I urge you to read that before coming back here:
This article is sort of a response to that: not because there’s anything wrong there (far from it), but because it made me start to think about the approaches I normally take, and more importantly, why.
The Simple Approach
Dart provides a pretty clean and simple way of serialising enums in the first place, assuming you don’t care whether or not your json is easily human readable: the index
of the enum value, and the values
of the enum class.
When an enum is declared, each value is given an index
field:
enum Colours { red, green, blue }
Here red
is implicitly assigned the index 0
, green the index 1
and blue the index 2
.
Theses indices can be used to reference the value within the values
list of the enum itself:
print(Colours.values[1]);
> Colours.green
And so, it’s easy to see how this naively gives us a way to serialise the enum. To use Luke’s car example:
One downside of this approach is that the resultant json now has an integer in the brand field rather than a string, which is less easy to interpret for any humans reading:
{
id: 1,
name: 'Cinquecento',
brand: 6
}
where 6
represents Fiat, but not all that obviously.
A greater downside is that, should the order of members of the enum change between serialisation and deserialisation, we’re stuffed: the index no longer refers to the correct value.
enum Colours { cyan, magenta, yellow, red, green, blue }print(Colours.values[1]); // was 'Colours.green' before
> Colours.magenta
So I often find myself creating what I tend to think of as Decorated Enums.
The Far More Complicated For No Good Reason Approach: Decorated Enums
Sometimes I want to be able to fiddle with the order of values in an enum, and augment (‘decorate’) them with extra functionality. Treat the enum like a class, in fact. And there is, of course, a pretty easy way to do that:
For me, the downside here is that there’s a fair bit of boiler plate to get it all up and running. (I’ve tried various things: auto-adding an instance to the values list on creation, for example [which fails because of Dart’s lazy object creation]; or using a map of name to object for the values [which I didn’t like because of even more repetition of the name string — YMMV]; and so on).
However, I think it’s worth it, not least because there are advantages in adding functionality to the enum itself. Where something lives in the brand, it should be found in the brand IMHO.
And there you have it. No more deserialisation problems if the order of the enum changes, and a bit of extra functionality living where it should in the enum, isGood
. The downside of the extra boiler plate is worth living with, IMHO. Again, YMMV.
Thanks for reading. Keep playing with Flutter.