From f8260e91545eeaa6fb05bfc44891f53e8178abee Mon Sep 17 00:00:00 2001 From: Eric <5089238+emizzle@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:03:17 +1100 Subject: [PATCH] Update readme --- README.md | 329 +++++++++++++++++++++++++++++++++++++++++++++- serde.nimble | 4 +- tests/test.nimble | 4 +- 3 files changed, 331 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ba3e977..6a406c9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,327 @@ -# nim-json -Drop-in replacement for std/json, with easy-to-use json serialization capabilities. +# 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 +type MyObj {.serialize(mode=OptOut).} = object + field1 {.serialize("mykey").}: bool + field2: bool + +# equivalent to + +type MyObj {.serialize(mode=OptOut).} = object + field1 {.serialize(key="mykey", ignore=true).}: bool + field2: bool +``` + +### `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` |