Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions docs/examples/reference0.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ type
var conf = defaultJsonReaderConf
conf.nestedDepthLimit = 0

let native =
Json.decode(rawJson, NimServer, flags = defaultJsonReaderFlags, conf = conf)

# decode into native Nim
#let native = Json.decode(rawJson, NimServer)
let native = Json.decode(rawJson, NimServer)

# decode into mixed Nim + JsonValueRef
let mixed = Json.decode(rawJson, MixedServer)
Expand Down
18 changes: 18 additions & 0 deletions docs/examples/streamwrite0.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import json_serialization, faststreams/outputs

let file = fileOutput("output.json")
var writer = JsonWriter[DefaultFlavor].init(file, pretty = true)

writer.beginArray()

for i in 0 ..< 2:
writer.beginObjectElement()

writer.writeMember("id", i)
writer.writeMember("name", "item" & $i)

writer.endObjectElement()

writer.endArray()

file.close()
16 changes: 16 additions & 0 deletions docs/examples/streamwrite1.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import json_serialization, faststreams/outputs

let file = fileOutput("output.json")
var writer = JsonWriter[DefaultFlavor].init(file)

# ANCHOR: Nesting
writer.writeObject:
writer.writeMember("status", "ok")
writer.writeArrayMember("data"):
for i in 0 ..< 2:
writer.writeObjectElement:
writer.writeMember("id", i)
writer.writeMember("name", "item" & $i)
# ANCHOR_END: Nesting

file.close()
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# User guide

- [Getting started](./getting_started.md)
- [Streaming](./streaming.md)
- [Reference](./reference.md)

# Developer guide
Expand Down
5 changes: 1 addition & 4 deletions docs/src/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ You can adjust these defaults to suit your needs:

### Common API

Similar to parsing, the [common serialization API]() is used to produce JSON documents.
Similar to parsing, the [common serialization API](https://github.com/status-im/nim-serialization?tab=readme-ov-file#common-api) is used to produce JSON documents.

```nim
{{#include ../examples/reference0.nim:Encode}}
Expand Down Expand Up @@ -257,9 +257,6 @@ writeField(w: var JsonWriter, name: string, value: auto)
iterator stepwiseArrayCreation[C](w: var JsonWriter, collection: C): auto
writeIterable(w: var JsonWriter, collection: auto)
writeArray[T](w: var JsonWriter, elements: openArray[T])

writeNumber[F,T](w: var JsonWriter[F], value: JsonNumber[T])
writeJsonValueRef[F,T](w: var JsonWriter[F], value: JsonValueRef[T])
```

## Enums
Expand Down
65 changes: 65 additions & 0 deletions docs/src/streaming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Streaming

`JsonWriter` can be used to incrementally write JSON data.

Incremental processing is ideal for large documents or when you want to avoid building the entire JSON structure in memory.

<!-- toc -->

## Writing

You can use `JsonWriter` to write JSON objects, arrays, and values step by step, directly to a file or any output stream.

The process is similar to when you override `writeValue` to provide custom serialization.

### Example: Writing a JSON Array of Objects

Suppose you want to write a large array of objects to a file, one at a time:

```nim
{{#include ../examples/streamwrite0.nim}}
```

Resulting file (`output.json`):
```json
[
{
"id": 0,
"name": "item0"
},
{
"id": 1,
"name": "item1"
}
]
```

```admonish warning "Elements in objects and array"
In the example, we see `beginArray`, `beginElement` and `writeMember`. The functions follow a pattern:
* functions without suffix, like `beginArray`, are used at the top-level
* functions with `Element` suffix are used inside arrays
* functions with `Member` suffix and accomanying name are used in objects

Thus, if we were writing an array inside another array, we would have used `beginArray` for the outer array and `beginArrayMember` for the inner array. These rules also apply when implementing `writeValue`.
```

### Example: Writing Nested Structures

Objects and arrays can be nested arbitrarily.

Here is the same array of JSON objects, nested in an envelope containing an additional `status` field.

Instead of manually placing `begin`/`end` pairs, we're using the convenience helpers `writeObjectElement` and `writeArrayMember`, along with `writeElement` to manage the required element markers:

```nim
{{#include ../examples/streamwrite1.nim:Nesting}}
```

This produces a the following output - notice the more compact representation when `pretty = true` is not used:
```json
{"status":"ok","data":[{"id":0,"name":"item0"},{"id":1,"name":"item1"}]}
```

```admonish tip
Similar to `begin`, we're using the `Element` suffix in arrays!
```
26 changes: 14 additions & 12 deletions json_serialization/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# This file may not be copied, modified, or distributed except according to
# those terms.

{.push gcsafe, raises: [].}

import
std/tables,
serialization/errors
Expand All @@ -18,13 +20,13 @@ export
type
JsonError* = object of SerializationError

# This is a special type to parse whatever
# json value into string.
JsonString* = distinct string
## A string containing valid JSON.
## Used to preserve and pass on parts of a JSON document to another parser
## or layer without interpreting it further

# This is a special type to parse whatever
# json value into nothing/skip it.
JsonVoid* = object
## Marker used for skipping a JSON value during parsing

JsonSign* {.pure.} = enum
None
Expand Down Expand Up @@ -63,11 +65,11 @@ type
stringLengthLimit*: int

JsonValueKind* {.pure.} = enum
String,
Number,
Object,
Array,
Bool,
String
Number
Object
Array
Bool
Null

JsonObjectType*[T: string or uint64] = OrderedTable[string, JsonValueRef[T]]
Expand All @@ -88,7 +90,6 @@ type
of JsonValueKind.Null:
discard


const
minPortableInt* = -9007199254740991 # -2**53 + 1
maxPortableInt* = 9007199254740991 # +2**53 - 1
Expand All @@ -110,8 +111,6 @@ const
stringLengthLimit: 0,
)

{.push gcsafe, raises: [].}

template `==`*(lhs, rhs: JsonString): bool =
string(lhs) == string(rhs)

Expand Down Expand Up @@ -172,4 +171,7 @@ func `==`*(lhs, rhs: JsonValueRef): bool =
of JsonValueKind.Null:
true

template `$`*(s: JsonString): string =
string(s)

{.pop.}
Loading