# nim-serde Easy-to-use json serialization capabilities, and a drop-in replacement for `std/json`. ## Quick examples Opt-in serialization by default: ```nim import pkg/serde/json type MyType = object field1 {.serialize.}: bool field2: bool assert MyType(field1: true, field2: true).toJson == """{"field1":true}""" ``` Opt-out deserialization by default: ```nim import pkg/serde/json # All fields deserialized, as none are ignored type MyType1 = object field1: bool field2: bool let jsn1 = """{ "field1": true, "field2": true }""" assert !MyType1.fromJson(jsn1) == MyType1(field1: true, field2: true) # Don't deserialize ignored fields in OptOut mode type MyType2 = object field1 {.deserialize(ignore=true).}: bool field2: bool let jsn2 = """{ "field1": true, "field2": true, "extra": "extra fields don't error in OptOut mode" }""" assert !MyType2.fromJson(jsn2) == MyType2(field1: false, field2: true) # Note, the ! operator is part of https://github.com/codex-storage/questionable, which retrieves a value if set ``` Serialize all fields of a type (OptOut mode): ```nim import pkg/serde/json type MyType {.serialize.} = object field1: int field2: int assert MyType(field1: 1, field2: 2).toJson == """{"field1":1,"field2":2}""" ``` Alias field names in both directions! ```nim import pkg/serde/json type MyType {.serialize.} = object field1 {.serialize("othername"),deserialize("takesprecedence").}: int field2: int assert MyType(field1: 1, field2: 2).toJson == """{"othername":1,"field2":2}""" let jsn = """{ "othername": 1, "field2": 2, "takesprecedence": 3 }""" assert !MyType.fromJson(jsn) == MyType(field1: 3, field2: 2) ``` Supports strict mode, where type fields and json fields must match ```nim import pkg/serde/json type MyType {.deserialize(mode=Strict).} = object field1: int field2: int let jsn = """{ "field1": 1, "field2": 2, "extra": 3 }""" let res = MyType.fromJson(jsn) assert res.isFailure assert res.error of SerdeError assert res.error.msg == "json field(s) missing in object: {\"extra\"}" ``` ## Serde modes `nim-serde` uses three different modes to control de/serialization: ```nim 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: ```nim 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. ```nim # 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, ```nim 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` |