Change parseJson to JsonNode.parse (#4)

* Change parseJson to JsonNode.parse

Exporting `parseJson` causes symbol clashes in downstream repos that import std/json, so changing the signature completely avoid this clash.

* Fix usages of parseJson, update README
This commit is contained in:
Eric 2024-02-09 11:08:14 +11:00 committed by GitHub
parent 6bd69489a7
commit b04435fb88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 29 deletions

View File

@ -108,7 +108,10 @@ OptOut
Strict
```
Modes can be set in the `{.serialize.}` and/or `{.deserialize.}` pragmas on type definitions. Each mode has a different meaning depending on if the type is being serialized or deserialized. Modes can be set by setting `mode` in the `serialize` or `deserialize` pragma annotation, eg:
Modes can be set in the `{.serialize.}` and/or `{.deserialize.}` pragmas on type
definitions. Each mode has a different meaning depending on if the type is being
serialized or deserialized. Modes can be set by setting `mode` in the `serialize` or
`deserialize` pragma annotation, eg:
```nim
type MyType {.serialize(mode=Strict).} = object
@ -126,7 +129,11 @@ type MyType {.serialize(mode=Strict).} = object
## Default modes
`nim-serde` will de/serialize types if they are not annotated with `serialize` or `deserialize`, but will assume a default mode. By default, with no pragmas specified, `serde` will always serialize in `OptIn` mode, meaning any fields to b Additionally, if the types are annotated, but a mode is not specified, `serde` will assume a (possibly different) default mode.
`nim-serde` will de/serialize types if they are not annotated with `serialize` or
`deserialize`, but will assume a default mode. By default, with no pragmas specified,
`serde` will always serialize in `OptIn` mode, meaning any fields to b Additionally, if
the types are annotated, but a mode is not specified, `serde` will assume a (possibly
different) default mode.
```nim
# Type is not annotated
@ -152,7 +159,8 @@ type MyObj {.serialize, deserialize.} = object
| Default (pragma, but no mode) | `OptOut` | `OptOut` |
## Serde field options
Type fields can be annotated with `{.serialize.}` and `{.deserialize.}` and properties can be set on these pragmas, determining de/serialization behavior.
Type fields can be annotated with `{.serialize.}` and `{.deserialize.}` and properties
can be set on these pragmas, determining de/serialization behavior.
For example,
@ -192,7 +200,9 @@ assert !Person.fromJson(createResponse) == Person(id: 1)
```
### `key`
Specifying a `key`, will alias the field name. When seriazlizing, json will be written with `key` instead of the field name. When deserializing, the json must contain `key` for the field to be deserialized.
Specifying a `key`, will alias the field name. When seriazlizing, json will be written
with `key` instead of the field name. When deserializing, the json must contain `key`
for the field to be deserialized.
### `ignore`
Specifying `ignore`, will prevent de/serialization on the field.
@ -207,7 +217,8 @@ Specifying `ignore`, will prevent de/serialization on the field.
## Deserialization
`serde` deserializes using `fromJson`, and in all instances returns `Result[T, CatchableError]`, where `T` is the type being deserialized. For example:
`serde` deserializes using `fromJson`, and in all instances returns `Result[T,
CatchableError]`, where `T` is the type being deserialized. For example:
```nim
type MyType = object
@ -266,7 +277,8 @@ func fromJson(_: type Address, json: JsonNode): ?!Address =
## Serializing to string (`toJson`)
`toJson` is a shortcut for serializing an object into its serialized string representation:
`toJson` is a shortcut for serializing an object into its serialized string
representation:
```nim
import pkg/serde/json
@ -289,9 +301,11 @@ return RestApiResponse.response(availability.toJson,
## `std/json` drop-in replacment
`nim-serde` can be used as a drop-in replacement for the [standard library's `json` module](https://nim-lang.org/docs/json.html), with a few notable improvements.
`nim-serde` can be used as a drop-in replacement for the [standard library's `json`
module](https://nim-lang.org/docs/json.html), with a few notable improvements.
Instead of importing `std/json` into your application, `pkg/serde/json` can be imported instead:
Instead of importing `std/json` into your application, `pkg/serde/json` can be imported
instead:
```diff
- import std/json
@ -316,7 +330,8 @@ expected["hello"] = newJString("world")
assert %*{"hello": "world"} == expected
```
As well, serialization of types can be overridden, and serialization of custom types can be introduced. Here, we are overriding the serialization of `int`:
As well, serialization of types can be overridden, and serialization of custom types can
be introduced. Here, we are overriding the serialization of `int`:
```nim
import pkg/serde/json
@ -329,7 +344,8 @@ assert 1.toJson == "2"
## `parseJson` and exception tracking
Unfortunately, `std/json`'s `parseJson` can raise an `Exception`, so proper exception tracking breaks, eg
Unfortunately, `std/json`'s `parseJson` can raise an `Exception`, so proper exception
tracking breaks, eg
```nim
@ -352,7 +368,10 @@ proc parseMe(me: string): JsonNode =
assert """{"hello":"world"}""".parseMe == %* { "hello": "world" }
```
This is due to `std/json`'s `parseJson` incorrectly raising `Exception`. This can be worked around by instead importing `serde` and calling its `parseJson`. Note that `serde`'s `parseJson` returns a `Result[JsonNode, CatchableError]` instead of just a plain `JsonNode` object:
This is due to `std/json`'s `parseJson` incorrectly raising `Exception`. This can be
worked around by instead importing `serde` and calling its `JsonNode.parse` routine.
Note that `serde`'s `JsonNode.parse` returns a `Result[JsonNode, CatchableError]`
instead of just a plain `JsonNode` object as in `std/json`'s `parseJson`:
```nim
import pkg/serde/json
@ -363,7 +382,7 @@ type
MyAppError = object of CatchableError
proc parseMe(me: string): JsonNode {.raises: [MyAppError].} =
without parsed =? me.parseJson, error:
without parsed =? JsonNode.parse(me), error:
raise newException(MyAppError, error.msg)
parsed

View File

@ -229,8 +229,8 @@ proc fromJson*[T: ref object or object](_: type T, json: JsonNode): ?!T =
success(res)
proc fromJson*(_: type JsonNode, jsn: string): ?!JsonNode =
return parser.parseJson(jsn)
proc fromJson*(_: type JsonNode, json: string): ?!JsonNode =
return JsonNode.parse(json)
proc fromJson*[T: ref object or object](_: type T, bytes: openArray[byte]): ?!T =
let json = string.fromBytes(bytes)
@ -238,6 +238,6 @@ proc fromJson*[T: ref object or object](_: type T, bytes: openArray[byte]): ?!T
echo "typeof json after parse: ", typeof json
T.fromJson(json)
proc fromJson*[T: ref object or object](_: type T, jsn: string): ?!T =
let jsn = ?parser.parseJson(jsn) # full qualification required in-module only
proc fromJson*[T: ref object or object](_: type T, json: string): ?!T =
let jsn = ?JsonNode.parse(json) # full qualification required in-module only
T.fromJson(jsn)

View File

@ -6,8 +6,9 @@ import ./types
{.push raises: [].}
proc parseJson*(json: string): ?!JsonNode =
## fix for nim raising Exception
proc parse*(_: type JsonNode, json: string): ?!JsonNode =
# Used as a replacement for `std/json.parseJson`. Will not raise Exception like in the
# standard library
try:
return stdjson.parseJson(json).catch
except Exception as e:

View File

@ -49,7 +49,7 @@ suite "json serialization - deserialize":
test "deserializes array to sequence":
let expected = @[1, 2, 3]
let json = !"[1,2,3]".parseJson
let json = !JsonNode.parse("[1,2,3]")
check !seq[int].fromJson(json) == expected
test "deserializes uints int.high or smaller":
@ -71,11 +71,13 @@ suite "json serialization - deserialize":
let expected = MyObj(mystring: "abc", myint: 123, myoption: some true)
let json =
!"""{
!JsonNode.parse(
"""{
"mystring": "abc",
"myint": 123,
"myoption": true
}""".parseJson
}"""
)
check !MyObj.fromJson(json) == expected
@ -87,10 +89,12 @@ suite "json serialization - deserialize":
let expected = MyObj(mystring: "abc", mybool: true)
let json =
!"""{
!JsonNode.parse(
"""{
"mystring": "abc",
"mybool": true
}""".parseJson
}"""
)
check !MyObj.fromJson(json) == expected
@ -102,11 +106,13 @@ suite "json serialization - deserialize":
let expected = MyObj(mystring: "abc", mybool: true)
let json =
!"""{
!JsonNode.parse(
"""{
"mystring": "abc",
"mybool": true,
"extra": "extra"
}""".parseJson
}"""
)
check !MyObj.fromJson(json) == expected
test "deserializes objects with less fields":
@ -117,9 +123,11 @@ suite "json serialization - deserialize":
let expected = MyObj(mystring: "abc", mybool: false)
let json =
!"""{
!JsonNode.parse(
"""{
"mystring": "abc"
}""".parseJson
}"""
)
check !MyObj.fromJson(json) == expected
test "deserializes ref objects":
@ -130,10 +138,12 @@ suite "json serialization - deserialize":
let expected = MyRef(mystring: "abc", myint: 1)
let json =
!"""{
!JsonNode.parse(
"""{
"mystring": "abc",
"myint": 1
}""".parseJson
}"""
)
let deserialized = !MyRef.fromJson(json)
check deserialized.mystring == expected.mystring