Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Enum JSON string converter #9881

Closed
caspiano opened this issue Nov 4, 2020 · 2 comments · Fixed by #10431
Closed

[RFC] Enum JSON string converter #9881

caspiano opened this issue Nov 4, 2020 · 2 comments · Fixed by #10431

Comments

@caspiano
Copy link
Contributor

caspiano commented Nov 4, 2020

Enums sure are handy for DSLs and protocols, especially with the recent discriminator logic in serialization.
Currently, if an enum is present on a model including JSON::Serializable it serializes to an integer.

I propose Enum::StringConverter JSON converter module.

Usage:

require "json"

struct Body
  include JSON::Serializable

  @[JSON::Field(converter: Enum::StringConverter)]
  getter hint : Example = Example::MemberOne
end

Body.new.to_json                         # => {"hint":"member_one"}
Body.from_json(%({"hint":"member_one"})) # => Body(@hint=Example::MemberOne)

An alternative would be allowing String typed enums, like the following

enum Example : String
  MemberOne
  MemberTwo
end

Example::MemberOne.to_json      # => "member_one"
Example.from_json("member_one") # => Example::MemberOne
@straight-shoota
Copy link
Member

A string-based converter seems like a great idea. I'm recalling that this was mentioned somewhere before, but can't find it.
We might even consider to make this the default serialization for enums. The currently used enum numbers are in general just an internal representation. In Crystal, enum members also just report their names, not the number.
There is actually a serious problem with using the numbers for serialization: Unless explicitly assigned (like in HTTP::Status), the values can easily change between builds when members are added, removed or reordered.
For example:

enum Foo
  B
  C
end

Foo::C.to_json # => "1"

When reading that serialized value from a program with an additional enum member, it returns a different result:

enum Foo
  B
  C
end

Foo.from_json("1") # => B

Using the names instead of values would be more reliable. Renaming an existing member is far less likely then adding, removing or reordering members. Deserializing a renamed or removed member would also properly fail, not simply return a different member.

@straight-shoota
Copy link
Member

To not forget: The same applies to YAML serialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants