Skip to content

Commit fadb42f

Browse files
committed
docs: move to separate folder, render with mdbook
1 parent dfbe7e1 commit fadb42f

File tree

18 files changed

+1159
-295
lines changed

18 files changed

+1159
-295
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ jobs:
1313
test-command: |
1414
env NIMLANG=c nimble test
1515
env NIMLANG=cpp nimble test
16+
17+
env nimble examples

.github/workflows/docs.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Docgen
2+
on:
3+
push:
4+
branches:
5+
- master
6+
- docs
7+
workflow_dispatch:
8+
9+
jobs:
10+
build:
11+
timeout-minutes: 20
12+
13+
name: 'Generate & upload documentation'
14+
runs-on: 'ubuntu-latest'
15+
continue-on-error: true
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
with:
20+
submodules: true
21+
- uses: Swatinem/rust-cache@v2
22+
- uses: jiro4989/setup-nim-action@v1
23+
with:
24+
nim-version: '2.2.4'
25+
26+
- name: Generate doc
27+
run: |
28+
nim --version
29+
nimble --version
30+
nimble mdbook
31+
nimble docs || true
32+
33+
- name: Deploy
34+
uses: peaceiris/actions-gh-pages@v3
35+
with:
36+
github_token: ${{ secrets.GITHUB_TOKEN }}
37+
publish_dir: ./docs/book
38+
force_orphan: true

README.md

Lines changed: 34 additions & 294 deletions
Original file line numberDiff line numberDiff line change
@@ -5,320 +5,60 @@
55
![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
66
![Github action](https://github.com/status-im/nim-json-serialization/workflows/CI/badge.svg)
77

8-
Flexible JSON serialization does not rely on run-time type information.
8+
## Introduction
99

10-
## Overview
11-
nim-json-serialization offers rich features on top of [nim-serialization](https://github.com/status-im/nim-serialization)
12-
framework. The following is available but not an exhaustive list of features:
10+
<!-- ANCHOR: Features -->
1311

14-
- Decode into Nim data types efficiently without an intermediate token.
15-
- Able to parse full spec of JSON including the notorious JSON number.
16-
- Support stdlib/JsonNode out of the box.
17-
- While stdlib/JsonNode does not support the full spec of the JSON number, we offer an alternative `JsonValueRef`.
18-
- Skipping JSON value is an efficient process, no token is generated at all and at the same time, the grammar is checked.
19-
- Skipping is also free from custom serializer interference.
20-
- An entire JSON value can be parsed into a valid JSON document string. This string document can be parsed again without losing any information.
21-
- Custom serialization is easy and safe to implement with the help of many built-in parsers.
22-
- Nonstandard features are put behind flags. You can choose which features to switch on or off.
23-
- Because the intended usage of this library will be in a security-demanding application, we make sure malicious inputs will not crash
24-
this library through fuzz tests.
25-
- The user also can tweak certain limits of the lexer/parser behavior using the configuration object.
26-
- `createJsonFlavor` is a powerful way to prevent cross contamination between different subsystem using different custom serializar on the same type.
12+
`nim-json-serialization` is a library in the [nim-serialization](https://github.com/status-im/nim-serialization) family for turning Nim objects into JSON documents and back. Features include:
2713

28-
## Spec compliance
29-
nim-json-serialization implements [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259)
30-
JSON spec and pass these test suites:
14+
- Efficient coding of JSON documents directly to and from Nim data types
15+
- Full type-based customization of both parsing and formatting
16+
- Flavors for defining multiple JSON serialization styles per Nim type
17+
- Efficient skipping of tags and values for partial JSON parsing
18+
- Flexibility in mixing type-based and dynamic JSON access
19+
- Structured `JsonValueRef` node type for DOM-style access to parsed document
20+
- Flat `JsonString` type for passing nested JSON documents between abstraction layers
21+
- Seamless interoperability with [`std/json`](https://nim-lang.org/docs/json.html) and `JsonNode`
22+
- Full [RFC8259 spec compliance](https://datatracker.ietf.org/doc/html/rfc8259) including the notorious JSON number
23+
- Passes [JSONTestSuite](https://github.com/nst/JSONTestSuite)
24+
- Customizable parser strictness including support for non-standard extensions
25+
- Well-defined handling of malformed / malicious inputs with configurable parsing limits
26+
- Fuzzing and comprehensive manual test coverage
3127

32-
- [JSONTestSuite](https://github.com/nst/JSONTestSuite)
28+
<!-- ANCHOR_END: Features -->
3329

34-
## Switchable features
35-
Many of these switchable features are widely used features in various projects but are not standard JSON features.
36-
But you can access them using the flags:
30+
## Getting started
3731

38-
- **allowUnknownFields[=off]**: enable unknown fields to be skipped instead of throwing an error.
39-
- **requireAllFields[=off]**: if one of the required fields is missing, the serializer will throw an error.
40-
- **escapeHex[=off]**: JSON doesn't support `\xHH` escape sequence, but it is a common thing in many languages.
41-
- **relaxedEscape[=off]**: only '0x00'..'0x1F' can be prepended by escape char `\\`, turn this on and you can escape any char.
42-
- **portableInt[=off]**: set the limit of integer to `-2**53 + 1` and `+2**53 - 1`.
43-
- **trailingComma[=on]**: allow the presence of a trailing comma after the last object member or array element.
44-
- **allowComments[=on]**: JSON standard doesn't mention about comments. Turn this on to parse both C style comments of `//..EOL` and `/* .. */`.
45-
- **leadingFraction[=on]**: something like `.123` is not a valid JSON number, but its widespread usage sometimes creeps into JSON documents.
46-
- **integerPositiveSign[=on]**: `+123` is also not a valid JSON number, but since `-123` is a valid JSON number, why not parse it safely?
47-
48-
## Safety features
49-
You can modify these default configurations to suit your needs.
50-
51-
- **nestedDepthLimit: 512**: maximum depth of the nested structure, they are a combination of objects and arrays depth(0=disable).
52-
- **arrayElementsLimit: 0**: maximum number of allowed array elements(0=disable).
53-
- **objectMembersLimit: 0**: maximum number of key-value pairs in an object(0=disable).
54-
- **integerDigitsLimit: 128**: limit the maximum digits of the integer part of JSON number.
55-
- **fractionDigitsLimit: 128**: limit the maximum digits of faction part of JSON number.
56-
- **exponentDigitsLimit: 32**: limit the maximum digits of the exponent part of JSON number.
57-
- **stringLengthLimit: 0**: limit the maximum bytes of string(0=disable).
58-
59-
## Special types
60-
61-
- **JsonString**: Use this type if you want to parse a JSON value to a valid JSON document contained in a string.
62-
- **JsonVoid**: Use this type to skip a valid JSON value.
63-
- **JsonNumber**: Use this to parse a valid JSON number including the fraction and exponent part.
64-
- Please note that this type is a generic, it support `uint64` and `string` as generic param.
65-
- The generic param will define the integer and exponent part as `uint64` or `string`.
66-
- If the generic param is `uint64`, overflow can happen, or max digit limit will apply.
67-
- If the generic param is `string`, the max digit limit will apply.
68-
- The fraction part is always a string to keep the leading zero of the fractional number.
69-
- **JsonValueRef**: Use this type to parse any valid JSON value into something like stdlib/JsonNode.
70-
- `JsonValueRef` is using `JsonNumber` instead of `int` or `float` like stdlib/JsonNode.
71-
72-
## Flavor
73-
74-
While flags and limits are runtime configuration, flavor is a powerful compile time mechanism to prevent
75-
cross contamination between different custom serializer operated the same type. For example,
76-
`json-rpc` subsystem dan `json-rest` subsystem maybe have different custom serializer for the same `UInt256`.
77-
78-
Json-Flavor will make sure, the compiler picks the right serializer for the right subsystem.
79-
You can use `useDefaultSerializationIn` to add serializers of a flavor to a specific type.
80-
81-
```Nim
82-
# These are the parameters you can pass to `createJsonFlavor` to create a new flavor.
83-
84-
FlavorName: untyped
85-
mimeTypeValue = "application/json"
86-
automaticObjectSerialization = false
87-
requireAllFields = true
88-
omitOptionalFields = true
89-
allowUnknownFields = true
90-
skipNullFields = false
91-
```
92-
93-
```Nim
94-
type
95-
OptionalFields = object
96-
one: Opt[string]
97-
two: Option[int]
98-
99-
createJsonFlavor OptJson
100-
OptionalFields.useDefaultSerializationIn OptJson
101-
```
102-
103-
`omitOptionalFields` is used by the Writer to ignore fields with null value.
104-
`skipNullFields` is used by the Reader to ignore fields with null value.
105-
106-
## Decoder example
10732
```nim
108-
type
109-
NimServer = object
110-
name: string
111-
port: int
112-
113-
MixedServer = object
114-
name: JsonValueRef
115-
port: int
116-
117-
StringServer = object
118-
name: JsonString
119-
port: JsonString
120-
121-
# decode into native Nim
122-
var nim_native = Json.decode(rawJson, NimServer)
123-
124-
# decode into mixed Nim + JsonValueRef
125-
var nim_mixed = Json.decode(rawJson, MixedServer)
126-
127-
# decode any value into string
128-
var nim_string = Json.decode(rawJson, StringServer)
129-
130-
# decode any valid JSON
131-
var json_value = Json.decode(rawJson, JsonValueRef)
132-
```
133-
134-
## Load and save
135-
```Nim
136-
var server = Json.loadFile("filename.json", Server)
137-
var server_string = Json.loadFile("filename.json", JsonString)
138-
139-
Json.saveFile("filename.json", server)
33+
requires "json_serialization"
14034
```
14135

142-
## Objects
143-
Decoding an object can be achieved via the `parseObject` template.
144-
To parse the value, you can use one of the helper functions or use `readValue`.
145-
`readObject` and `readObjectFields` iterators are also handy when creating a custom object parser.
146-
147-
```Nim
148-
proc readValue*(r: var JsonReader, table: var Table[string, int]) =
149-
parseObject(r, key):
150-
table[key] = r.parseInt(int)
151-
```
152-
153-
## Sets and list-like
154-
Similar to `Object`, sets and list or array-like data structures can be parsed using
155-
`parseArray` template. It comes in two variations, indexed and non-indexed.
156-
157-
Built-in `readValue` for regular `seq` and `array` is implemented for you.
158-
No built-in `readValue` for `set` or `set-like` is provided, you must overload it yourself depending on your need.
36+
Create a type and use it to encode and decode JSON:
15937

16038
```nim
161-
type
162-
HoldArray = object
163-
data: array[3, int]
164-
165-
HoldSeq = object
166-
data: seq[int]
39+
import json_serialization
16740
168-
WelderFlag = enum
169-
TIG
170-
MIG
171-
MMA
41+
type Request = object
42+
jsonrpc: string
43+
`method`: string
17244
173-
Welder = object
174-
flags: set[WelderFlag]
45+
let
46+
json = """{"jsonrpc": "2.0", "method": "name"}"""
47+
decoded = Json.decode(json, Request)
17548
176-
proc readValue*(r: var JsonReader, value: var HoldArray) =
177-
# parseArray with index, `i` can be any valid identifier
178-
r.parseArray(i):
179-
value.data[i] = r.parseInt(int)
180-
181-
proc readValue*(r: var JsonReader, value: var HoldSeq) =
182-
# parseArray without index
183-
r.parseArray:
184-
let lastPos = value.data.len
185-
value.data.setLen(lastPos + 1)
186-
readValue(r, value.data[lastPos])
187-
188-
proc readValue*(r: var JsonReader, value: var Welder) =
189-
# populating set also okay
190-
r.parseArray:
191-
value.flags.incl r.parseInt(int).WelderFlag
192-
```
193-
194-
## Custom iterators
195-
Using these custom iterators, you can have access to sub-token elements.
196-
197-
```Nim
198-
customIntValueIt(r: var JsonReader; body: untyped)
199-
customNumberValueIt(r: var JsonReader; body: untyped)
200-
customStringValueIt(r: var JsonReader; limit: untyped; body: untyped)
201-
customStringValueIt(r: var JsonReader; body: untyped)
202-
```
203-
## Convenience iterators
204-
205-
```Nim
206-
readArray(r: var JsonReader, ElemType: typedesc): ElemType
207-
readObjectFields(r: var JsonReader, KeyType: type): KeyType
208-
readObjectFields(r: var JsonReader): string
209-
readObject(r: var JsonReader, KeyType: type, ValueType: type): (KeyType, ValueType)
49+
echo decoded.jsonrpc
50+
echo Json.encode(decoded, pretty=true)
21051
```
21152

212-
## Helper procs
213-
When crafting a custom serializer, use these parsers, they are safe and intuitive.
214-
Avoid using the lexer directly.
53+
## Documentation
21554

216-
```Nim
217-
tokKind(r: var JsonReader): JsonValueKind
218-
parseString(r: var JsonReader, limit: int): string
219-
parseString(r: var JsonReader): string
220-
parseBool(r: var JsonReader): bool
221-
parseNull(r: var JsonReader)
222-
parseNumber(r: var JsonReader, T: type): JsonNumber[T: string or uint64]
223-
parseNumber(r: var JsonReader, val: var JsonNumber)
224-
toInt(r: var JsonReader, val: JsonNumber, T: type SomeInteger, portable: bool): T
225-
parseInt(r: var JsonReader, T: type SomeInteger, portable: bool = false): T
226-
toFloat(r: var JsonReader, val: JsonNumber, T: type SomeFloat): T
227-
parseFloat(r: var JsonReader, T: type SomeFloat): T
228-
parseAsString(r: var JsonReader, val: var string)
229-
parseAsString(r: var JsonReader): JsonString
230-
parseValue(r: var JsonReader, T: type): JsonValueRef[T: string or uint64]
231-
parseValue(r: var JsonReader, val: var JsonValueRef)
232-
parseArray(r: var JsonReader; body: untyped)
233-
parseArray(r: var JsonReader; idx: untyped; body: untyped)
234-
parseObject(r: var JsonReader, key: untyped, body: untyped)
235-
parseObjectWithoutSkip(r: var JsonReader, key: untyped, body: untyped)
236-
parseObjectSkipNullFields(r: var JsonReader, key: untyped, body: untyped)
237-
parseObjectCustomKey(r: var JsonReader, keyAction: untyped, body: untyped)
238-
parseJsonNode(r: var JsonReader): JsonNode
239-
skipSingleJsValue(r: var JsonReader)
240-
readRecordValue[T](r: var JsonReader, value: var T)
241-
```
242-
243-
## Helper procs of JsonWriter
244-
245-
```Nim
246-
beginRecord(w: var JsonWriter, T: type)
247-
beginRecord(w: var JsonWriter)
248-
endRecord(w: var JsonWriter)
249-
250-
writeObject(w: var JsonWriter, T: type)
251-
writeObject(w: var JsonWriter)
252-
253-
writeFieldName(w: var JsonWriter, name: string)
254-
writeField(w: var JsonWriter, name: string, value: auto)
255-
256-
iterator stepwiseArrayCreation[C](w: var JsonWriter, collection: C): auto
257-
writeIterable(w: var JsonWriter, collection: auto)
258-
writeArray[T](w: var JsonWriter, elements: openArray[T])
259-
260-
writeNumber[F,T](w: var JsonWriter[F], value: JsonNumber[T])
261-
writeJsonValueRef[F,T](w: var JsonWriter[F], value: JsonValueRef[T])
262-
```
55+
See the [user guide](https://status-im.github.io/nim-json-serialization/).
26356

264-
## Enums
57+
## Contributing
26558

266-
```Nim
267-
type
268-
Fruit = enum
269-
Apple = "Apple"
270-
Banana = "Banana"
59+
Contributions are welcome - please make sure to add test coverage for features and fixes!
27160

272-
Drawer = enum
273-
One
274-
Two
275-
276-
Number = enum
277-
Three = 3
278-
Four = 4
279-
280-
Mixed = enum
281-
Six = 6
282-
Seven = "Seven"
283-
```
284-
285-
nim-json-serialization automatically detect which representation an enum should be parsed.
286-
The detection occurs when parse JSON literal and from the enum declaration itself.
287-
'Fruit' expect string literal. 'Drawer' or 'Number' expect numeric literal.
288-
'Mixed' is disallowed. If the json literal does not match the expected enum style,
289-
exception will be raised. But you can configure individual enum type with:
290-
291-
```Nim
292-
configureJsonDeserialization(
293-
T: type[enum], allowNumericRepr: static[bool] = false,
294-
stringNormalizer: static[proc(s: string): string] = strictNormalize)
295-
296-
# example:
297-
Mixed.configureJsonDeserialization(allowNumericRepr = true) # only at top level
298-
```
299-
300-
When encode an enum, user is also given flexibility to configure at Flavor level
301-
or for individual enum type.
302-
303-
```Nim
304-
type
305-
EnumRepresentation* = enum
306-
EnumAsString
307-
EnumAsNumber
308-
EnumAsStringifiedNumber
309-
310-
# examples:
311-
312-
# Flavor level
313-
Json.flavorEnumRep(EnumAsString) # default flavor, can be called from non top level
314-
Flavor.flavorEnumRep(EnumAsNumber) # custom flavor, can be called from non top level
315-
316-
# individual enum type no matter what flavor
317-
Fruit.configureJsonSerialization(EnumAsNumber) # only at top level
318-
319-
# individual enum type of specific flavor
320-
MyJson.flavorEnumRep(Drawer, EnumAsString) # only at top level
321-
```
61+
`json_serialization` follows the [Status Nim Style Guide](https://status-im.github.io/nim-style-guide/).
32262

32363
## License
32464

0 commit comments

Comments
 (0)