diff --git a/docs/examples/getstarted1.nim b/docs/examples/getstarted1.nim index b2244c1..46ccaf3 100644 --- a/docs/examples/getstarted1.nim +++ b/docs/examples/getstarted1.nim @@ -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 diff --git a/docs/examples/getstarted2.nim b/docs/examples/getstarted2.nim index 0ad351d..e4314bf 100644 --- a/docs/examples/getstarted2.nim +++ b/docs/examples/getstarted2.nim @@ -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 diff --git a/docs/src/reference.md b/docs/src/reference.md index 441d0fc..de88221 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -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. @@ -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 diff --git a/json_serialization/pkg/chronos.nim b/json_serialization/pkg/chronos.nim new file mode 100644 index 0000000..754f960 --- /dev/null +++ b/json_serialization/pkg/chronos.nim @@ -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) diff --git a/json_serialization/pkg/results.nim b/json_serialization/pkg/results.nim index b911c0d..e4cc4c8 100644 --- a/json_serialization/pkg/results.nim +++ b/json_serialization/pkg/results.nim @@ -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 diff --git a/json_serialization/std/net.nim b/json_serialization/std/net.nim index 0ac2596..7d2ef74 100644 --- a/json_serialization/std/net.nim +++ b/json_serialization/std/net.nim @@ -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)) @@ -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.} diff --git a/json_serialization/std/options.nim b/json_serialization/std/options.nim index 1f16a7f..27b2f6b 100644 --- a/json_serialization/std/options.nim +++ b/json_serialization/std/options.nim @@ -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) diff --git a/json_serialization/std/sets.nim b/json_serialization/std/sets.nim index 45d46e8..b1084a1 100644 --- a/json_serialization/std/sets.nim +++ b/json_serialization/std/sets.nim @@ -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 diff --git a/json_serialization/std/tables.nim b/json_serialization/std/tables.nim index 87b4b0a..1b77dac 100644 --- a/json_serialization/std/tables.nim +++ b/json_serialization/std/tables.nim @@ -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) @@ -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) diff --git a/json_serialization/std/uri.nim b/json_serialization/std/uri.nim new file mode 100644 index 0000000..171672c --- /dev/null +++ b/json_serialization/std/uri.nim @@ -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.} diff --git a/json_serialization/writer.nim b/json_serialization/writer.nim index 451f816..5932c9d 100644 --- a/json_serialization/writer.nim +++ b/json_serialization/writer.nim @@ -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]) =