|
1 | 1 | # nim-serde |
2 | 2 |
|
3 | | -A serialization and deserialization library for Nim supporting multiple wire formats. |
| 3 | +A serialization and deserialization library for Nim supporting multiple formats. |
4 | 4 |
|
5 | | -## Supported Wire Formats |
| 5 | +## Supported Serialization Formats |
6 | 6 |
|
7 | | -nim-serde currently supports the following wire formats: |
| 7 | +nim-serde currently supports the following serialization formats: |
8 | 8 |
|
9 | 9 | - **JSON**: A text-based data interchange format. [See JSON](serde/json/README.md) for details. |
10 | 10 | - **CBOR**: A binary data format following RFC 8949. [See CBOR](serde/cbor/README.md) for details. |
11 | 11 |
|
12 | | -## Serde modes |
| 12 | +## Quick Examples |
13 | 13 |
|
14 | | -`nim-serde` uses three different modes to control de/serialization: |
| 14 | +### JSON Serialization and Deserialization |
15 | 15 |
|
16 | 16 | ```nim |
17 | | -OptIn |
18 | | -OptOut |
19 | | -Strict |
20 | | -``` |
21 | | - |
22 | | -Modes can be set in the `{.serialize.}` and/or `{.deserialize.}` pragmas on type |
23 | | -definitions. Each mode has a different meaning depending on if the type is being |
24 | | -serialized or deserialized. Modes can be set by setting `mode` in the `serialize` or |
25 | | -`deserialize` pragma annotation, eg: |
26 | | - |
27 | | -```nim |
28 | | -type MyType {.serialize(mode=Strict).} = object |
29 | | - field1: bool |
30 | | - field2: bool |
31 | | -``` |
32 | | - |
33 | | -### Modes reference |
34 | | - |
35 | | -| | serialize | deserialize | |
36 | | -|:-------------------|:------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
37 | | -| `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. | |
38 | | -| `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. | |
39 | | -| `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. | |
| 17 | +import ./serde/json |
| 18 | +import questionable/results |
| 19 | +
|
| 20 | +# Define a type |
| 21 | +type Person = object |
| 22 | + name {.serialize.}: string |
| 23 | + age {.serialize.}: int |
| 24 | + address: string # Not serialized by default in OptIn mode |
| 25 | +
|
| 26 | +# Create an instance |
| 27 | +let person = Person(name: "John Doe", age: 30, address: "123 Main St") |
| 28 | +
|
| 29 | +# Serialization |
| 30 | +echo "JSON Serialization Example" |
| 31 | +let jsonString = person.toJson(pretty = true) |
| 32 | +echo jsonString |
| 33 | +
|
| 34 | +# Verify serialization output |
| 35 | +let expectedJson = """{ |
| 36 | + "name": "John Doe", |
| 37 | + "age": 30 |
| 38 | +}""" |
| 39 | +assert jsonString == expectedJson |
40 | 40 |
|
41 | | -## Default modes |
| 41 | +# Deserialization |
| 42 | +echo "\nJSON Deserialization Example" |
| 43 | +let jsonData = """{"name":"Jane Doe","age":28,"address":"456 Oak Ave"}""" |
| 44 | +let result = Person.fromJson(jsonData) |
42 | 45 |
|
43 | | -`nim-serde` will de/serialize types if they are not annotated with `serialize` or |
44 | | -`deserialize`, but will assume a default mode. By default, with no pragmas specified, |
45 | | -`serde` will always serialize in `OptIn` mode, meaning any fields to b Additionally, if |
46 | | -the types are annotated, but a mode is not specified, `serde` will assume a (possibly |
47 | | -different) default mode. |
| 46 | +# check if deserialization was successful |
| 47 | +assert result.isSuccess |
48 | 48 |
|
49 | | -```nim |
50 | | -# Type is not annotated |
51 | | -# A default mode of OptIn (for serialize) and OptOut (for deserialize) is assumed. |
| 49 | +# get the deserialized value |
| 50 | +let parsedPerson = !result |
52 | 51 |
|
53 | | -type MyObj = object |
54 | | - field1: bool |
55 | | - field2: bool |
| 52 | +echo parsedPerson |
| 53 | +#[ |
| 54 | +Expected Output: |
| 55 | +Person( |
| 56 | + name: "Jane Doe", |
| 57 | + age: 28, |
| 58 | + address: "456 Oak Ave" |
| 59 | +) |
| 60 | +]# |
56 | 61 |
|
57 | | -# Type is annotated, but mode not specified |
58 | | -# A default mode of OptOut is assumed for both serialize and deserialize. |
59 | | -
|
60 | | -type MyObj {.serialize, deserialize.} = object |
61 | | - field1: bool |
62 | | - field2: bool |
63 | 62 | ``` |
64 | 63 |
|
65 | | -### Default mode reference |
66 | | - |
67 | | -| | serialize | deserialize | |
68 | | -|:------------------------------|:----------|:------------| |
69 | | -| Default (no pragma) | `OptIn` | `OptOut` | |
70 | | -| Default (pragma, but no mode) | `OptOut` | `OptOut` | |
71 | | - |
72 | | -## Serde field options |
73 | | -Type fields can be annotated with `{.serialize.}` and `{.deserialize.}` and properties |
74 | | -can be set on these pragmas, determining de/serialization behavior. |
75 | | - |
76 | | -For example, |
| 64 | +### CBOR Serialization and Deserialization |
77 | 65 |
|
78 | 66 | ```nim |
79 | | -import pkg/serde/json |
80 | | -
|
81 | | -type |
82 | | - Person {.serialize(mode=OptOut), deserialize(mode=OptIn).} = object |
83 | | - id {.serialize(ignore=true), deserialize(key="personid").}: int |
84 | | - name: string |
85 | | - birthYear: int |
86 | | - address: string |
87 | | - phone: string |
88 | | -
|
89 | | -let person = Person( |
90 | | - name: "Lloyd Christmas", |
91 | | - birthYear: 1970, |
92 | | - address: "123 Sesame Street, Providence, Rhode Island 12345", |
93 | | - phone: "555-905-justgivemethedamnnumber!⛽️🔥") |
94 | | -
|
95 | | -let createRequest = """{ |
96 | | - "name": "Lloyd Christmas", |
97 | | - "birthYear": 1970, |
98 | | - "address": "123 Sesame Street, Providence, Rhode Island 12345", |
99 | | - "phone": "555-905-justgivemethedamnnumber!⛽️🔥" |
100 | | -}""" |
101 | | -assert person.toJson(pretty=true) == createRequest |
102 | | -
|
103 | | -let createResponse = """{ |
104 | | - "personid": 1, |
105 | | - "name": "Lloyd Christmas", |
106 | | - "birthYear": 1970, |
107 | | - "address": "123 Sesame Street, Providence, Rhode Island 12345", |
108 | | - "phone": "555-905-justgivemethedamnnumber!⛽️🔥" |
109 | | -}""" |
110 | | -assert !Person.fromJson(createResponse) == Person(id: 1) |
111 | | -``` |
112 | | - |
113 | | -### `key` |
114 | | -Specifying a `key`, will alias the field name. When seriazlizing, json will be written |
115 | | -with `key` instead of the field name. When deserializing, the json must contain `key` |
116 | | -for the field to be deserialized. |
117 | | - |
118 | | -### `ignore` |
119 | | -Specifying `ignore`, will prevent de/serialization on the field. |
120 | | - |
121 | | -### Serde field options reference |
122 | | - |
123 | | -| | serialize | deserialize | |
124 | | -|:---------|:-----------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------| |
125 | | -| `key` | aliases the field name in json | deserializes the field if json contains `key` | |
126 | | -| `ignore` | <li>**OptOut:** field not serialized</li><li>**OptIn:** field not serialized</li><li>**Strict:** field serialized</li> | <li>**OptOut:** field not deserialized</li><li>**OptIn:** field not deserialized</li><li>**Strict:** field deserialized</li> | |
127 | | - |
| 67 | +import ./serde/cbor |
| 68 | +import questionable/results |
| 69 | +import std/streams |
| 70 | +
|
| 71 | +# Define a type |
| 72 | +type Person = object |
| 73 | + name: string |
| 74 | + age: int |
| 75 | + address: string |
| 76 | +
|
| 77 | +# Create an instance |
| 78 | +let person = Person(name: "John Doe", age: 30, address: "123 Main St") |
| 79 | +
|
| 80 | +# Serialization using Stream API |
| 81 | +echo "CBOR Stream API Serialization" |
| 82 | +let stream = newStringStream() |
| 83 | +let writeResult = stream.writeCbor(person) |
| 84 | +assert writeResult.isSuccess |
| 85 | +
|
| 86 | +# Serialization using toCbor function |
| 87 | +echo "\nCBOR toCbor Function Serialization" |
| 88 | +let cborResult = toCbor(person) |
| 89 | +assert cborResult.isSuccess |
| 90 | +
|
| 91 | +let serializedCbor = !cborResult |
| 92 | +
|
| 93 | +# Deserialization |
| 94 | +echo "\nCBOR Deserialization" |
| 95 | +let personResult = Person.fromCbor(serializedCbor) |
| 96 | +
|
| 97 | +# check if deserialization was successful |
| 98 | +assert personResult.isSuccess |
| 99 | +
|
| 100 | +# get the deserialized value |
| 101 | +let parsedPerson = !personResult |
| 102 | +echo parsedPerson |
| 103 | +
|
| 104 | +#[ |
| 105 | +Expected Output: |
| 106 | +Person( |
| 107 | + name: "John Doe", |
| 108 | + age: 30, |
| 109 | + address: "123 Main St" |
| 110 | +) |
| 111 | +]# |
128 | 112 |
|
| 113 | +``` |
129 | 114 |
|
| 115 | +Refer to the [json](serde/json/README.md) and [cbor](serde/cbor/README.md) files for more comprehensive examples. |
130 | 116 |
|
131 | 117 | ## Known Issues |
132 | 118 |
|
|
0 commit comments