status-im / nim-serialization

A modern and extensible serialization framework for Nim
62 stars 8 forks source link

Enums with string values are not supported #28

Closed ghost closed 4 years ago

ghost commented 4 years ago

Example

import serialization, json_serialization

type
  ChatMemberStatusKind* {.pure.} = enum
    cmsCreator = "chatMemberStatusCreator",
    cmsRestricted = "chatMemberStatusRestricted",
    cmsMember = "chatMemberStatusMember",
    cmsAdministrator = "chatMemberStatusAdministrator",
    cmsBanned = "chatMemberStatusBanned",
    cmsLeft = "chatMemberStatusLeft",

  Chat = ref object
    case kind* {.serializedFieldName("@type").}: ChatMemberStatusKind
    of cmsCreator:
      data: int
    else:
      discard

let data = """
{
  "@type": "chatMemberStatusCreator",
  "data": 5
}"""

echo Json.decode(data, Chat)

Is this supposed to be supported or not? I'm asking because it's supported in json.to, but it lacks pragmas for specifying custom field names or for not serializing stuff (and doing that manually seems a little bit stupid, although I already did the decoding support with custom field names for json.to)

zah commented 4 years ago

Yes, this is the default behaviour. There was a more general issue triggered when the serializedFieldName pragma is used on a case object branch discriminator field.

To make the example work, pull the latest version and change the last line to echo Json.decode(data, Chat)[]. This is needed because echo cannot print ref types by default.

zah commented 4 years ago

For any future readers stumbling here, I would also note that the behaviour above is often questionable, but you can always specify the precise wanted treatment for your enum by defining overloads for readValue and writeValue:

For example, here is how the enum can be encoded as an integer:

import
  stew/objects, serialization, json_serialization

type
  ChatMemberStatusKind* {.pure.} = enum
    cmsCreator = "chatMemberStatusCreator",
    cmsRestricted = "chatMemberStatusRestricted",
    cmsMember = "chatMemberStatusMember",
    cmsAdministrator = "chatMemberStatusAdministrator",
    cmsBanned = "chatMemberStatusBanned",
    cmsLeft = "chatMemberStatusLeft",

  Chat = ref object
    case kind* {.serializedFieldName("@type").}: ChatMemberStatusKind
    of cmsMember:
      data: int
    else:
      discard

let data = """
{
  "@type": 2,
  "data": 5
}"""

proc writeValue*(w: var JsonWriter, val: ChatMemberStatusKind) =
  w.writeValue int(val)

proc readValue*(r: var JsonReader, val: var ChatMemberStatusKind) =
  if not checkedEnumAssign(val, r.readValue(int)):
    r.raiseUnexpectedValue("Invalid value for the ChatMemberStatusKind enum")

echo Json.decode(data, Chat)[]