Skip to content
Merged
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
9 changes: 5 additions & 4 deletions docs/examples/getstarted1.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import json_serialization
type JsonRpcId = distinct JsonString

proc readValue*(
r: var JsonReader, val: var JsonRpcId
r: var JsonReader, value: var JsonRpcId
) {.raises: [IOError, JsonReaderError].} =
let tok = r.tokKind
case tok
of JsonValueKind.Number, JsonValueKind.String, JsonValueKind.Null:
# Keep the original value without further processing
val = JsonRpcId(r.parseAsString())
value = JsonRpcId(r.parseAsString())
else:
r.raiseUnexpectedValue("Invalid RequestId, got " & $tok)

proc writeValue*(w: var JsonWriter, val: JsonRpcId) {.raises: [IOError].} =
w.writeValue(JsonString(val)) # Preserve the original content
proc writeValue*(w: var JsonWriter, value: JsonRpcId) {.raises: [IOError].} =
w.writeValue(JsonString(value)) # Preserve the original content

# ANCHOR_END: Custom

type Request = object
Expand Down
8 changes: 4 additions & 4 deletions docs/examples/getstarted2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ createJsonFlavor JrpcSys,
type JsonRpcId = distinct JsonString

proc readValue*(
r: var JsonReader[JrpcSys], val: var JsonRpcId
r: var JsonReader[JrpcSys], value: var JsonRpcId
) {.raises: [IOError, JsonReaderError].} =
let tok = r.tokKind
case tok
of JsonValueKind.Number, JsonValueKind.String, JsonValueKind.Null:
# Keep the original value without further processing
val = JsonRpcId(r.parseAsString())
value = JsonRpcId(r.parseAsString())
else:
r.raiseUnexpectedValue("Invalid RequestId, got " & $tok)

proc writeValue*(w: var JsonWriter[JrpcSys], val: JsonRpcId) {.raises: [IOError].} =
w.writeValue(JsonString(val)) # Preserve the original content
proc writeValue*(w: var JsonWriter[JrpcSys], value: JsonRpcId) {.raises: [IOError].} =
w.writeValue(JsonString(value)) # Preserve the original content
# ANCHOR_END: Custom

# ANCHOR: Request
Expand Down
12 changes: 6 additions & 6 deletions docs/src/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,12 @@ Parsing and writing can be customized by providing overloads for the `readValue`

```nim
# Custom serializers for MyType should match the following signatures
proc readValue*(r: var JsonReader, v: var MyType) {.raises: [IOError, SerializationError].}
proc writeValue*(w: var JsonWriter, v: MyType) {.raises: [IOError].}
proc readValue*(r: var JsonReader, value: var MyType) {.raises: [IOError, SerializationError].}
proc writeValue*(w: var JsonWriter, value: MyType) {.raises: [IOError].}

# When flavors are used, add the flavor as well
proc readValue*(r: var JsonReader[MyFlavor], v: var MyType) {.raises: [IOError, SerializationError].}
proc writeValue*(w: var JsonWriter[MyFlavor], v: MyType) {.raises: [IOError].}
proc readValue*(r: var JsonReader[MyFlavor], value: var MyType) {.raises: [IOError, SerializationError].}
proc writeValue*(w: var JsonWriter[MyFlavor], value: MyType) {.raises: [IOError].}
```

The JsonReader provides access to the JSON token stream coming out of the lexer. While the token stream can be accessed directly, there are several helpers that make it easier to correctly parse common JSON shapes.
Expand All @@ -148,9 +148,9 @@ The JsonReader provides access to the JSON token stream coming out of the lexer.
Decode objects using the `parseObject` template. To parse values, use helper functions or `readValue`. The `readObject` and `readObjectFields` iterators are also useful for custom object parsers.

```nim
proc readValue*(r: var JsonReader, table: var Table[string, int]) =
proc readValue*(r: var JsonReader, value: var Table[string, int]) =
parseObject(r, key):
table[key] = r.parseInt(int)
value[key] = r.parseInt(int)
```

### Sets and List-like Types
Expand Down
29 changes: 29 additions & 0 deletions json_serialization/pkg/chronos.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# json-serialization
# Copyright (c) 2019-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.

{.push raises: [], gcsafe.}

import ../../json_serialization/[reader, writer], ../std/net

from chronos/transports/common import
TransportAddress, TransportAddressError, initTAddress, `$`

export TransportAddress, net

proc writeValue*(w: var JsonWriter, value: TransportAddress) {.raises: [IOError].} =
w.writeValue($value)

proc readValue*(
r: var JsonReader, value: var TransportAddress
) {.raises: [IOError, SerializationError].} =
let s = r.readValue(string)
try:
value = initTAddress(s)
except TransportAddressError as exc:
r.raiseUnexpectedValue("Cannot parse TransportAddress: " & exc.msg)
28 changes: 13 additions & 15 deletions json_serialization/pkg/results.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,31 @@

{.push raises: [], gcsafe.}

import
pkg/results, ../../json_serialization/[reader, writer, lexer]
import pkg/results, ../../json_serialization/[reader, writer, lexer]

export
results
export results

template shouldWriteObjectField*[T](field: Result[T, void]): bool =
template shouldWriteObjectField*[T](field: Opt[T]): bool =
field.isOk

proc writeValue*[T](
writer: var JsonWriter, value: Result[T, void]) {.raises: [IOError].} =
proc writeValue*[T](w: var JsonWriter, value: Opt[T]) {.raises: [IOError].} =
mixin writeValue

if value.isOk:
writer.writeValue value.get
w.writeValue(value.get)
else:
writer.writeValue JsonString("null")
w.writeValue JsonString("null")

proc readValue*[T](reader: var JsonReader, value: var Result[T, void]) {.
raises: [IOError, SerializationError].} =
proc readValue*[T](
r: var JsonReader, value: var Opt[T]
) {.raises: [IOError, SerializationError].} =
mixin readValue

if reader.tokKind == JsonValueKind.Null:
if r.tokKind == JsonValueKind.Null:
reset value
reader.parseNull()
r.parseNull()
else:
value.ok reader.readValue(T)
value.ok r.readValue(T)

func isFieldExpected*[T, E](_: type[Result[T, E]]): bool {.compileTime.} =
func isFieldExpected*[T](_: type[Opt[T]]): bool {.compileTime.} =
false
45 changes: 18 additions & 27 deletions json_serialization/std/net.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# json-serialization
# json_serialization
# Copyright (c) 2019-2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
Expand All @@ -9,35 +9,26 @@

{.push raises: [], gcsafe.}

import
std/[net, strutils],
chronos/transports/common,
../../json_serialization
import ../../json_serialization, std/net
export net

export
net, common
when compiles((; import chronos/transports/common)):
# Backwards-compat with json_ser <= 0.4.2
import ../pkg/chronos as jschronos
export jschronos

proc writeValue*(
writer: var JsonWriter, value: IpAddress) {.raises: [IOError].} =
writeValue(writer, $value)
proc writeValue*(w: var JsonWriter, value: IpAddress) {.raises: [IOError].} =
w.writeValue($value)

proc readValue*(reader: var JsonReader, value: var IpAddress) =
let s = reader.readValue(string)
proc readValue*(
r: var JsonReader, value: var IpAddress
) {.raises: [IOError, SerializationError].} =
let s = r.readValue(string)
try:
value = parseIpAddress s
except CatchableError:
raiseUnexpectedValue(reader, "Invalid IP address")
value = parseIpAddress(s)
except ValueError as exc:
r.raiseUnexpectedValue(exc.msg)

proc writeValue*(
writer: var JsonWriter, value: Port) {.raises: [IOError].} =
writeValue(writer, uint16 value)
Port.serializesAsBase(Json)

proc readValue*(reader: var JsonReader, value: var Port) =
value = Port reader.readValue(uint16)

proc writeValue*(
writer: var JsonWriter, value: AddressFamily) {.raises: [IOError].} =
writeValue(writer, $value)

proc readValue*(reader: var JsonReader, value: var AddressFamily) =
value = parseEnum[AddressFamily](reader.readValue(string))
{.pop.}
17 changes: 9 additions & 8 deletions json_serialization/std/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@ export options
template shouldWriteObjectField*(field: Option): bool =
field.isSome

proc writeValue*(writer: var JsonWriter, value: Option) {.raises: [IOError].} =
proc writeValue*(w: var JsonWriter, value: Option) {.raises: [IOError].} =
mixin writeValue

if value.isSome:
writer.writeValue value.get
w.writeValue value.get
else:
writer.writeValue JsonString("null")
w.writeValue JsonString("null")

proc readValue*[T](reader: var JsonReader, value: var Option[T]) {.
raises: [IOError, SerializationError].} =
proc readValue*[T](
r: var JsonReader, value: var Option[T]
) {.raises: [IOError, SerializationError].} =
mixin readValue

if reader.tokKind == JsonValueKind.Null:
if r.tokKind == JsonValueKind.Null:
reset value
reader.parseNull()
r.parseNull()
else:
value = some reader.readValue(T)
value = some r.readValue(T)
14 changes: 7 additions & 7 deletions json_serialization/std/sets.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
import stew/shims/sets, ../../json_serialization/[reader, writer, lexer]
export sets

type
SetType = OrderedSet | HashSet | set
type SetType = OrderedSet | HashSet | set

proc writeValue*(writer: var JsonWriter, value: SetType) {.raises: [IOError].} =
writer.writeIterable value
proc writeValue*(w: var JsonWriter, value: SetType) {.raises: [IOError].} =
w.writeIterable value

proc readValue*(reader: var JsonReader, value: var SetType) {.
raises: [IOError, SerializationError].} =
proc readValue*(
r: var JsonReader, value: var SetType
) {.raises: [IOError, SerializationError].} =
type ElemType = type(value.items)
value = init SetType
for elem in readArray(reader, ElemType):
for elem in r.readArray(ElemType):
value.incl elem
29 changes: 12 additions & 17 deletions json_serialization/std/tables.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,19 @@

{.push raises: [], gcsafe.}

import
std/strutils,
stew/shims/tables,
../../json_serialization/[reader, writer, lexer]
import std/strutils, stew/shims/tables, ../../json_serialization/[reader, writer, lexer]

export tables

type
TableType = OrderedTable | Table
type TableType = OrderedTable | Table

proc writeValue*(
writer: var JsonWriter, value: TableType) {.raises: [IOError].} =
writer.beginRecord()
for key, val in value:
writer.writeField $key, val
writer.endRecord()
proc writeValue*(w: var JsonWriter, value: TableType) {.raises: [IOError].} =
w.writeObject:
for key, val in value:
w.writeField $key, val

template to*(a: string, b: typed): untyped =
{.error: "doesnt support keys with type " & $type(b) .}
{.error: "doesnt support keys with type " & $type(b).}

template to*(a: string, b: type int): int =
parseInt(a)
Expand All @@ -38,13 +32,14 @@ template to*(a: string, b: type float): float =
template to*(a: string, b: type string): string =
a

proc readValue*(reader: var JsonReader, value: var TableType) {.
raises: [IOError, SerializationError].} =
proc readValue*(
r: var JsonReader, value: var TableType
) {.raises: [IOError, SerializationError].} =
try:
type KeyType = type(value.keys)
type ValueType = type(value.values)
value = init TableType
for key, val in readObject(reader, string, ValueType):
for key, val in r.readObject(string, ValueType):
value[to(key, KeyType)] = val
except ValueError as ex:
reader.raiseUnexpectedValue("TableType: " & ex.msg)
r.raiseUnexpectedValue("TableType: " & ex.msg)
27 changes: 27 additions & 0 deletions json_serialization/std/uri.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# json_serialization
# Copyright (c) 2025 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.

{.push raises: [], gcsafe.}

import ../../json_serialization, std/uri
export uri

proc writeValue*(w: var JsonWriter, value: Uri) {.raises: [IOError].} =
w.writeValue($value)

proc readValue*(
r: var JsonReader, value: var Uri
) {.raises: [IOError, SerializationError].} =
let s = r.readValue(string)
try:
value = parseUri(s)
except ValueError as exc:
r.raiseUnexpectedValue(exc.msg)

{.pop.}
4 changes: 2 additions & 2 deletions json_serialization/writer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,8 @@ template endRecord*(w: var JsonWriter) = w.endObject()

template serializesAsTextInJson*(T: type[enum]) =
## Configure an enum type to serialize as text in JSON.
template writeValue*(w: var JsonWriter, val: T) =
w.writeValue $val
template writeValue*(w: var JsonWriter, value: T) =
w.writeValue $value

template configureJsonSerialization*(
T: type[enum], enumRep: static[EnumRepresentation]) =
Expand Down
Loading