nim-serde
A serialization and deserialization library for Nim supporting multiple wire formats.
Supported Wire Formats
nim-serde currently supports the following wire formats:
- JSON: A text-based data interchange format. See JSON for details.
- CBOR: A binary data format following RFC 8949. See CBOR for details.
Serde modes
nim-serde uses three different modes to control de/serialization:
OptIn
OptOut
Strict
Modes can be set in the {.serialize.} and/or {.deserialize.} pragmas on type
definitions. Each mode has a different meaning depending on if the type is being
serialized or deserialized. Modes can be set by setting mode in the serialize or
deserialize pragma annotation, eg:
type MyType {.serialize(mode=Strict).} = object
field1: bool
field2: bool
Modes reference
| serialize | deserialize | |
|---|---|---|
SerdeMode.OptOut |
All object fields will be serialized, except fields marked with {.serialize(ignore=true).}. |
All json keys will be deserialized, except fields marked with {.deserialize(ignore=true).}. No error if extra json fields exist. |
SerdeMode.OptIn |
Only fields marked with {.serialize.} will be serialized. Fields marked with {.serialize(ignore=true).} will not be serialized. |
Only fields marked with {.deserialize.} will be deserialized. Fields marked with {.deserialize(ignore=true).} will not be deserialized. A SerdeError error is raised if the field is missing in json. |
SerdeMode.Strict |
All object fields will be serialized, regardless if the field is marked with {.serialize(ignore=true).}. |
Object fields and json fields must match exactly, otherwise a SerdeError is raised. |
Default modes
nim-serde will de/serialize types if they are not annotated with serialize or
deserialize, but will assume a default mode. By default, with no pragmas specified,
serde will always serialize in OptIn mode, meaning any fields to b Additionally, if
the types are annotated, but a mode is not specified, serde will assume a (possibly
different) default mode.
# Type is not annotated
# A default mode of OptIn (for serialize) and OptOut (for deserialize) is assumed.
type MyObj = object
field1: bool
field2: bool
# Type is annotated, but mode not specified
# A default mode of OptOut is assumed for both serialize and deserialize.
type MyObj {.serialize, deserialize.} = object
field1: bool
field2: bool
Default mode reference
| serialize | deserialize | |
|---|---|---|
| Default (no pragma) | OptIn |
OptOut |
| Default (pragma, but no mode) | OptOut |
OptOut |
Serde field options
Type fields can be annotated with {.serialize.} and {.deserialize.} and properties
can be set on these pragmas, determining de/serialization behavior.
For example,
import pkg/serde/json
type
Person {.serialize(mode=OptOut), deserialize(mode=OptIn).} = object
id {.serialize(ignore=true), deserialize(key="personid").}: int
name: string
birthYear: int
address: string
phone: string
let person = Person(
name: "Lloyd Christmas",
birthYear: 1970,
address: "123 Sesame Street, Providence, Rhode Island 12345",
phone: "555-905-justgivemethedamnnumber!⛽️🔥")
let createRequest = """{
"name": "Lloyd Christmas",
"birthYear": 1970,
"address": "123 Sesame Street, Providence, Rhode Island 12345",
"phone": "555-905-justgivemethedamnnumber!⛽️🔥"
}"""
assert person.toJson(pretty=true) == createRequest
let createResponse = """{
"personid": 1,
"name": "Lloyd Christmas",
"birthYear": 1970,
"address": "123 Sesame Street, Providence, Rhode Island 12345",
"phone": "555-905-justgivemethedamnnumber!⛽️🔥"
}"""
assert !Person.fromJson(createResponse) == Person(id: 1)
key
Specifying a key, will alias the field name. When seriazlizing, json will be written
with key instead of the field name. When deserializing, the json must contain key
for the field to be deserialized.
ignore
Specifying ignore, will prevent de/serialization on the field.
Serde field options reference
| serialize | deserialize | |
|---|---|---|
key |
aliases the field name in json | deserializes the field if json contains key |
ignore |
Known Issues
There is a known issue when using mixins with generic overloaded procs like fromJson. At the time of mixin call, only the fromJson overloads in scope of the called mixin are available to be dispatched at runtime. There could be other fromJson overloads declared in other modules, but are not in scope at the time the mixin was called.
Therefore, anytime fromJson is called targeting a declared overload, it may or may not be dispatchable. This can be worked around by forcing the fromJson overload into scope at compile time. For example, in your application where the fromJson overload is defined, at the bottom of the module add:
static: MyType.fromJson("")
This will ensure that the MyType.fromJson overload is dispatchable.
The basic types that serde supports should already have their overloads forced in scope in the deserializer module.
For an illustration of the problem, please see this narrow example by @gmega.