mirror of
https://github.com/logos-storage/nim-serde.git
synced 2026-01-10 17:43:08 +00:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
649ae60e05 | ||
|
|
ca4457a8b0 | ||
|
|
1c0f313662 | ||
|
|
60fcbdab3c | ||
|
|
5ced7c88b9 | ||
|
|
1ce89b206e | ||
|
|
69a7a0111a | ||
|
|
7f9622fa5c | ||
|
|
fc4ef51b81 | ||
|
|
83e4a2ccf6 | ||
|
|
b2f4dbcd25 | ||
|
|
f5af686a85 | ||
|
|
69c630212c | ||
|
|
9ec0e0d5a6 | ||
|
|
c81b751602 | ||
|
|
cf59b42ed5 | ||
|
|
b39497be32 | ||
|
|
71f30d4678 | ||
|
|
6bf626cab4 | ||
|
|
cdba47becf | ||
|
|
7fb79610c4 | ||
|
|
10271bd494 | ||
|
|
9dd16685d1 | ||
|
|
6d2fc9406a | ||
|
|
d84641333a | ||
|
|
d726f6c6eb | ||
|
|
b7995afb35 | ||
|
|
34e28c33d9 | ||
|
|
bd262054f2 | ||
|
|
f9f4b3b662 | ||
|
|
2fcdc9e9c5 | ||
|
|
2c0518bed0 | ||
|
|
3957da5b52 | ||
|
|
1cedad7488 | ||
|
|
b955985548 | ||
|
|
414a23b52f | ||
|
|
419d48c8d3 | ||
|
|
baa847e937 | ||
|
|
cefbc47367 | ||
|
|
fbabe954b4 | ||
|
|
b1e5e5d39a | ||
|
|
a4a6b0d949 | ||
|
|
0b29da6e98 | ||
|
|
6e79374c87 | ||
|
|
06b271a727 | ||
|
|
88d00624ed | ||
|
|
a261c3d214 | ||
|
|
e67f7e4b04 | ||
|
|
1af86f84dc | ||
|
|
a2cca7788b | ||
|
|
956d4a2e57 | ||
|
|
9f36b95ac4 | ||
|
|
bd9c3d8340 | ||
|
|
96ae7e130e | ||
|
|
ae23a5f306 | ||
|
|
12b9ab6615 | ||
|
|
d59075df98 | ||
|
|
14492e31ea | ||
|
|
f70e14024e | ||
|
|
fe0e21c5a6 | ||
|
|
a71a7c39cd | ||
|
|
2d6ead6245 | ||
|
|
b04435fb88 | ||
|
|
6bd69489a7 | ||
|
|
42b7d91eb6 | ||
|
|
1b77afcbf1 | ||
|
|
d113c1e158 |
41
.github/workflows/changelog.yml
vendored
Normal file
41
.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: changelog
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
name: Generate changelog
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Create Token
|
||||
id: create_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.APP_ID }}
|
||||
private_key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: "✏️ Generate release changelog"
|
||||
uses: heinrichreimer/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ steps.create_token.outputs.token }}
|
||||
|
||||
- name: Commit CHANGELOG.md
|
||||
uses: planetscale/ghcommit-action@v0.1.33
|
||||
with:
|
||||
repo: ${{ github.repository }}
|
||||
branch: master
|
||||
commit_message: 'docs: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]'
|
||||
file_pattern: CHANGELOG.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.create_token.outputs.token }}
|
||||
36
.github/workflows/ci.yml
vendored
Normal file
36
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
nim: [2.0.16, 2.2.4]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nim
|
||||
uses: iffy/install-nim@v4
|
||||
with:
|
||||
version: ${{ matrix.nim }}
|
||||
- name: Build
|
||||
run: nimble install -y
|
||||
|
||||
- name: Test
|
||||
run: nimble test -y
|
||||
|
||||
status:
|
||||
if: always()
|
||||
needs: [test]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
|
||||
run: exit 1
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@ nimble.develop
|
||||
nimble.paths
|
||||
.idea
|
||||
vendor/
|
||||
.vscode/
|
||||
.vscode/
|
||||
nimbledeps
|
||||
|
||||
100
CHANGELOG.md
100
CHANGELOG.md
@ -1,6 +1,96 @@
|
||||
## v0.1.1
|
||||
Add `toJson(pretty=true)`, so that stringified json can be prettified 😊
|
||||
Formatted all nim files using [`nph`](https://github.com/arnetheduck/nph)
|
||||
# Changelog
|
||||
|
||||
## v0.1.0
|
||||
Initial versioned release
|
||||
## [Unreleased](https://github.com/logos-storage/nim-serde/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v.1.2.2...HEAD)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- chore: rename Codex to Logos Storage [\#34](https://github.com/logos-storage/nim-serde/pull/34) ([2-towns](https://github.com/2-towns))
|
||||
- Update chronicles [\#33](https://github.com/logos-storage/nim-serde/pull/33) ([markspanbroek](https://github.com/markspanbroek))
|
||||
- fix loglevel and add log scope to allow filtering and avoid log pollution [\#30](https://github.com/logos-storage/nim-serde/pull/30) ([dryajov](https://github.com/dryajov))
|
||||
- ci: add matrix status job [\#29](https://github.com/logos-storage/nim-serde/pull/29) ([veaceslavdoina](https://github.com/veaceslavdoina))
|
||||
|
||||
## [v.1.2.2](https://github.com/logos-storage/nim-serde/tree/v.1.2.2) (2024-10-23)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.2.1...v.1.2.2)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- v1.2.2 [\#28](https://github.com/logos-storage/nim-serde/pull/28) ([emizzle](https://github.com/emizzle))
|
||||
- chore: bumps nim from 1.6.16 to 1.6.20 in ci [\#27](https://github.com/logos-storage/nim-serde/pull/27) ([emizzle](https://github.com/emizzle))
|
||||
- chore: remove unneeded echos [\#26](https://github.com/logos-storage/nim-serde/pull/26) ([emizzle](https://github.com/emizzle))
|
||||
|
||||
## [v1.2.1](https://github.com/logos-storage/nim-serde/tree/v1.2.1) (2024-05-21)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.2.0...v1.2.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- v1.2.1 [\#25](https://github.com/logos-storage/nim-serde/pull/25) ([emizzle](https://github.com/emizzle))
|
||||
- fix: force symbol resolution for types that serde de/serializes [\#24](https://github.com/logos-storage/nim-serde/pull/24) ([emizzle](https://github.com/emizzle))
|
||||
- feat: improve deserialization from string [\#23](https://github.com/logos-storage/nim-serde/pull/23) ([emizzle](https://github.com/emizzle))
|
||||
- feat: improve stint parsing [\#22](https://github.com/logos-storage/nim-serde/pull/22) ([emizzle](https://github.com/emizzle))
|
||||
|
||||
## [v1.2.0](https://github.com/logos-storage/nim-serde/tree/v1.2.0) (2024-05-14)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.1.1...v1.2.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- chore: v1.2.0 [\#21](https://github.com/logos-storage/nim-serde/pull/21) ([emizzle](https://github.com/emizzle))
|
||||
- fix: add missing test update [\#20](https://github.com/logos-storage/nim-serde/pull/20) ([emizzle](https://github.com/emizzle))
|
||||
- chore: reorganize deserialize tests [\#19](https://github.com/logos-storage/nim-serde/pull/19) ([emizzle](https://github.com/emizzle))
|
||||
- fix: UInt256 not correctly deserializing from string [\#18](https://github.com/logos-storage/nim-serde/pull/18) ([emizzle](https://github.com/emizzle))
|
||||
|
||||
## [v1.1.1](https://github.com/logos-storage/nim-serde/tree/v1.1.1) (2024-05-13)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.1.0...v1.1.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- chore: v.1.1.1 [\#17](https://github.com/logos-storage/nim-serde/pull/17) ([emizzle](https://github.com/emizzle))
|
||||
- chore\[formatting\]: update formatting [\#16](https://github.com/logos-storage/nim-serde/pull/16) ([emizzle](https://github.com/emizzle))
|
||||
- add empty string test for UInt256 [\#15](https://github.com/logos-storage/nim-serde/pull/15) ([emizzle](https://github.com/emizzle))
|
||||
- Fix log topics [\#14](https://github.com/logos-storage/nim-serde/pull/14) ([benbierens](https://github.com/benbierens))
|
||||
- run changelog workflow on release [\#12](https://github.com/logos-storage/nim-serde/pull/12) ([emizzle](https://github.com/emizzle))
|
||||
|
||||
## [v1.1.0](https://github.com/logos-storage/nim-serde/tree/v1.1.0) (2024-02-14)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.0.0...v1.1.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- chore: v1.1.0 [\#11](https://github.com/logos-storage/nim-serde/pull/11) ([emizzle](https://github.com/emizzle))
|
||||
- deserialize non-prefixed stuint [\#10](https://github.com/logos-storage/nim-serde/pull/10) ([emizzle](https://github.com/emizzle))
|
||||
- deserialize seq\[T\] and Option\[T\] from string [\#9](https://github.com/logos-storage/nim-serde/pull/9) ([emizzle](https://github.com/emizzle))
|
||||
|
||||
## [v1.0.0](https://github.com/logos-storage/nim-serde/tree/v1.0.0) (2024-02-13)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v0.1.2...v1.0.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- v1.0.0 [\#8](https://github.com/logos-storage/nim-serde/pull/8) ([emizzle](https://github.com/emizzle))
|
||||
- fix: change serializer funcs to procs [\#7](https://github.com/logos-storage/nim-serde/pull/7) ([emizzle](https://github.com/emizzle))
|
||||
- Use token for changelog generator [\#6](https://github.com/logos-storage/nim-serde/pull/6) ([veaceslavdoina](https://github.com/veaceslavdoina))
|
||||
- Adjust workflows for changelog generation [\#5](https://github.com/logos-storage/nim-serde/pull/5) ([veaceslavdoina](https://github.com/veaceslavdoina))
|
||||
- Change parseJson to JsonNode.parse [\#4](https://github.com/logos-storage/nim-serde/pull/4) ([emizzle](https://github.com/emizzle))
|
||||
- Add CI workflow [\#3](https://github.com/logos-storage/nim-serde/pull/3) ([emizzle](https://github.com/emizzle))
|
||||
- Fix deserialization of openArray\[byte\] [\#2](https://github.com/logos-storage/nim-serde/pull/2) ([emizzle](https://github.com/emizzle))
|
||||
|
||||
## [v0.1.2](https://github.com/logos-storage/nim-serde/tree/v0.1.2) (2024-02-08)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v0.1.1...v0.1.2)
|
||||
|
||||
## [v0.1.1](https://github.com/logos-storage/nim-serde/tree/v0.1.1) (2024-02-07)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v0.1.0...v0.1.1)
|
||||
|
||||
## [v0.1.0](https://github.com/logos-storage/nim-serde/tree/v0.1.0) (2024-02-07)
|
||||
|
||||
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/5a8e85449d9320d2277bc9aadf1daae61c7f057b...v0.1.0)
|
||||
|
||||
|
||||
|
||||
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2024 Codex Storage
|
||||
Copyright (c) 2025 Logos Storage
|
||||
|
||||
Licensed and distributed under either of
|
||||
[MIT license](http://opensource.org/licenses/MIT) or
|
||||
|
||||
67
README.md
67
README.md
@ -44,7 +44,7 @@ let jsn2 = """{
|
||||
}"""
|
||||
assert !MyType2.fromJson(jsn2) == MyType2(field1: false, field2: true)
|
||||
|
||||
# Note, the ! operator is part of https://github.com/codex-storage/questionable, which retrieves a value if set
|
||||
# Note, the ! operator is part of https://github.com/logos-storage/questionable, which retrieves a value if set
|
||||
```
|
||||
|
||||
Serialize all fields of a type (OptOut mode):
|
||||
@ -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,9 +382,31 @@ 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
|
||||
|
||||
assert """{"hello":"world"}""".parseMe == %* { "hello": "world" }
|
||||
```
|
||||
|
||||
## Known issues
|
||||
|
||||
There is a known issue when using mixins with generic overloaded procs like
|
||||
`fromJson`. At the time of mixin call, only the `fromJson` overloads in scope of
|
||||
the called mixin are available to be dispatched at runtime. There could be other
|
||||
`fromJson` overloads declared in other modules, but are not in scope at the time
|
||||
the mixin was called. Therefore, anytime `fromJson` is called targeting a
|
||||
declared overload, it may or may not be dispatchable. This can be worked around
|
||||
by forcing the `fromJson` overload into scope at compile time. For example, in
|
||||
your application where the `fromJson` overload is defined, at the bottom of the
|
||||
module add:
|
||||
```nim
|
||||
static: MyType.fromJson("")
|
||||
```
|
||||
This will ensure that the `MyType.fromJson` overload is dispatchable.
|
||||
|
||||
The basic types that serde supports should already have their overloads forced
|
||||
in scope in [the `deserializer`
|
||||
module](./serde/json/deserializer.nim#L340-L356).
|
||||
|
||||
For an illustration of the problem, please see this [narrow example](https://github.com/gmega/serialization-bug/tree/main/narrow) by [@gmega](https://github.com/gmega).
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
--styleCheck:usages
|
||||
--styleCheck:error
|
||||
|
||||
# begin Nimble config (version 1)
|
||||
when fileExists("nimble.paths"):
|
||||
# begin Nimble config (version 2)
|
||||
--noNimblePath
|
||||
when withDir(thisDir(), system.fileExists("nimble.paths")):
|
||||
include "nimble.paths"
|
||||
# end Nimble config
|
||||
|
||||
130
nimble.lock
Normal file
130
nimble.lock
Normal file
@ -0,0 +1,130 @@
|
||||
{
|
||||
"version": 2,
|
||||
"packages": {
|
||||
"results": {
|
||||
"version": "0.5.1",
|
||||
"vcsRevision": "df8113dda4c2d74d460a8fa98252b0b771bf1f27",
|
||||
"url": "https://github.com/arnetheduck/nim-results",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [],
|
||||
"checksums": {
|
||||
"sha1": "a9c011f74bc9ed5c91103917b9f382b12e82a9e7"
|
||||
}
|
||||
},
|
||||
"unittest2": {
|
||||
"version": "0.2.4",
|
||||
"vcsRevision": "8b51e99b4a57fcfb31689230e75595f024543024",
|
||||
"url": "https://github.com/status-im/nim-unittest2",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [],
|
||||
"checksums": {
|
||||
"sha1": "746106a4dfefffce497f1693733f1c1513b5c62c"
|
||||
}
|
||||
},
|
||||
"stew": {
|
||||
"version": "0.4.1",
|
||||
"vcsRevision": "e5740014961438610d336cd81706582dbf2c96f0",
|
||||
"url": "https://github.com/status-im/nim-stew",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"results",
|
||||
"unittest2"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "996d9c058ee078d0209a5f539424a0235683918c"
|
||||
}
|
||||
},
|
||||
"faststreams": {
|
||||
"version": "0.4.1",
|
||||
"vcsRevision": "c3ac3f639ed1d62f59d3077d376a29c63ac9750c",
|
||||
"url": "https://github.com/status-im/nim-faststreams",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"stew",
|
||||
"unittest2"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "e2d03280cf0675314136e6e63d9d928d9dfc9705"
|
||||
}
|
||||
},
|
||||
"serialization": {
|
||||
"version": "0.4.9",
|
||||
"vcsRevision": "b5193007a49639b21b2b80cd8ef8cbe0df6b0e48",
|
||||
"url": "https://github.com/status-im/nim-serialization",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"faststreams",
|
||||
"unittest2",
|
||||
"stew"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "ab0812a7e16ae39ee68f4a6b1938a1b83dee34be"
|
||||
}
|
||||
},
|
||||
"json_serialization": {
|
||||
"version": "0.4.2",
|
||||
"vcsRevision": "e076cb9a2dff30e13aad0bb4a13049da86d07967",
|
||||
"url": "https://github.com/status-im/nim-json-serialization",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"serialization",
|
||||
"stew",
|
||||
"results"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "2b26a9e0fc79638dbb9272fb4ab5a1d79264f938"
|
||||
}
|
||||
},
|
||||
"testutils": {
|
||||
"version": "0.8.0",
|
||||
"vcsRevision": "e4d37dc1652d5c63afb89907efb5a5e812261797",
|
||||
"url": "https://github.com/status-im/nim-testutils",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"unittest2"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "d1678f50aa47d113b4e77d41eec2190830b523fa"
|
||||
}
|
||||
},
|
||||
"chronicles": {
|
||||
"version": "0.12.2",
|
||||
"vcsRevision": "27ec507429a4eb81edc20f28292ee8ec420be05b",
|
||||
"url": "https://github.com/status-im/nim-chronicles",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"faststreams",
|
||||
"serialization",
|
||||
"json_serialization",
|
||||
"testutils"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "02febb20d088120b2836d3306cfa21f434f88f65"
|
||||
}
|
||||
},
|
||||
"questionable": {
|
||||
"version": "0.10.15",
|
||||
"vcsRevision": "82d90b67bcfb7f2e918b61dace2ff1a4ced60935",
|
||||
"url": "https://github.com/logos-storage/questionable",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [],
|
||||
"checksums": {
|
||||
"sha1": "3238ff637c7b44d2fa8fcb839a8ded968e389de3"
|
||||
}
|
||||
},
|
||||
"stint": {
|
||||
"version": "0.8.2",
|
||||
"vcsRevision": "470b7892561b5179ab20bd389a69217d6213fe58",
|
||||
"url": "https://github.com/status-im/nim-stint",
|
||||
"downloadMethod": "git",
|
||||
"dependencies": [
|
||||
"stew",
|
||||
"unittest2"
|
||||
],
|
||||
"checksums": {
|
||||
"sha1": "d8f871fd617e7857192d4609fe003b48942a8ae5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tasks": {}
|
||||
}
|
||||
20
serde.nimble
20
serde.nimble
@ -1,20 +1,14 @@
|
||||
# Package
|
||||
|
||||
version = "0.1.2"
|
||||
author = "nim-serde authors"
|
||||
description = "Easy-to-use serialization capabilities (currently json only), with a drop-in replacement for std/json."
|
||||
license = "MIT"
|
||||
skipDirs = @["tests"]
|
||||
|
||||
version = "1.2.2"
|
||||
author = "nim-serde authors"
|
||||
description = "Easy-to-use serialization capabilities (currently json only)."
|
||||
license = "MIT"
|
||||
skipDirs = @["tests"]
|
||||
|
||||
# Dependencies
|
||||
requires "nim >= 1.6.14"
|
||||
requires "chronicles >= 0.10.3 & < 0.11.0"
|
||||
requires "chronicles >= 0.10.3"
|
||||
requires "questionable >= 0.10.13 & < 0.11.0"
|
||||
requires "stint"
|
||||
requires "stew"
|
||||
|
||||
task test, "Run the test suite":
|
||||
exec "nimble install -d -y"
|
||||
withDir "tests":
|
||||
exec "nimble test"
|
||||
requires "asynctest >= 0.5.1 & < 0.6.0"
|
||||
|
||||
@ -16,6 +16,7 @@ import ./errors
|
||||
import ./stdjson
|
||||
import ./pragmas
|
||||
import ./types
|
||||
import ./helpers
|
||||
|
||||
export parser
|
||||
export chronicles except toJson
|
||||
@ -28,7 +29,7 @@ export types
|
||||
{.push raises: [].}
|
||||
|
||||
logScope:
|
||||
topics = "json deserialization"
|
||||
topics = "nimserde json deserializer"
|
||||
|
||||
template expectJsonKind(
|
||||
expectedType: type, expectedKinds: set[JsonNodeKind], json: JsonNode
|
||||
@ -81,6 +82,12 @@ proc fromJson*[T: SomeInteger](_: type T, json: JsonNode): ?!T =
|
||||
expectJsonKind(T, {JInt, JString}, json)
|
||||
case json.kind
|
||||
of JString:
|
||||
if json.isNullString:
|
||||
let err = newSerdeError("Cannot deserialize 'null' into type " & $T)
|
||||
return failure(err)
|
||||
elif json.isEmptyString:
|
||||
return success T(0)
|
||||
|
||||
without x =? parseBiggestUInt(json.str).catch, error:
|
||||
return failure newSerdeError(error.msg)
|
||||
return success cast[T](x)
|
||||
@ -126,22 +133,35 @@ proc fromJson*[N: static[int], T: array[N, byte]](_: type T, json: JsonNode): ?!
|
||||
proc fromJson*[T: distinct](_: type T, json: JsonNode): ?!T =
|
||||
success T(?T.distinctBase.fromJson(json))
|
||||
|
||||
proc fromJson*[N: static[int], T: StUint[N]](_: type T, json: JsonNode): ?!T =
|
||||
expectJsonKind(T, JString, json)
|
||||
let jsonStr = json.getStr
|
||||
let prefix = jsonStr[0 .. 1].toLowerAscii
|
||||
case prefix
|
||||
of "0x":
|
||||
catch parse(jsonStr, T, 16)
|
||||
of "0o":
|
||||
catch parse(jsonStr, T, 8)
|
||||
of "0b":
|
||||
catch parse(jsonStr, T, 2)
|
||||
else:
|
||||
catch parse(jsonStr, T)
|
||||
proc fromJson*(T: typedesc[StUint or StInt], json: JsonNode): ?!T =
|
||||
expectJsonKind(T, {JString, JInt}, json)
|
||||
|
||||
case json.kind
|
||||
of JInt:
|
||||
return catch parse($json, T)
|
||||
else: # JString (only other kind allowed)
|
||||
if json.isNullString:
|
||||
let err = newSerdeError("Cannot deserialize 'null' into type " & $T)
|
||||
return failure(err)
|
||||
|
||||
let jsonStr = json.getStr
|
||||
let prefix =
|
||||
if jsonStr.len >= 2:
|
||||
jsonStr[0 .. 1].toLowerAscii
|
||||
else:
|
||||
jsonStr
|
||||
case prefix
|
||||
of "0x":
|
||||
catch parse(jsonStr, T, 16)
|
||||
of "0o":
|
||||
catch parse(jsonStr, T, 8)
|
||||
of "0b":
|
||||
catch parse(jsonStr, T, 2)
|
||||
else:
|
||||
catch parse(jsonStr, T)
|
||||
|
||||
proc fromJson*[T](_: type Option[T], json: JsonNode): ?!Option[T] =
|
||||
if json.isNil or json.kind == JNull:
|
||||
if json.isNil or json.kind == JNull or json.isEmptyString or json.isNullString:
|
||||
return success(none T)
|
||||
without val =? T.fromJson(json), error:
|
||||
return failure(error)
|
||||
@ -187,6 +207,10 @@ proc fromJson*[T: ref object or object](_: type T, json: JsonNode): ?!T =
|
||||
let isOptionalValue = typeof(value) is Option
|
||||
var skip = false # workaround for 'continue' not supported in a 'fields' loop
|
||||
|
||||
# logScope moved into proc due to chronicles issue: https://github.com/status-im/nim-chronicles/issues/148
|
||||
logScope:
|
||||
topics = "serde json deserialization"
|
||||
|
||||
case mode
|
||||
of Strict:
|
||||
if opts.key notin json:
|
||||
@ -196,16 +220,16 @@ proc fromJson*[T: ref object or object](_: type T, json: JsonNode): ?!T =
|
||||
warn "object field marked as 'ignore' while in Strict mode, field will be deserialized anyway"
|
||||
of OptIn:
|
||||
if not hasDeserializePragma:
|
||||
debug "object field not marked as 'deserialize', skipping"
|
||||
trace "object field not marked as 'deserialize', skipping"
|
||||
skip = true
|
||||
elif opts.ignore:
|
||||
debug "object field marked as 'ignore', skipping"
|
||||
trace "object field marked as 'ignore', skipping"
|
||||
skip = true
|
||||
elif opts.key notin json and not isOptionalValue:
|
||||
return failure newSerdeError("object field missing in json: " & opts.key)
|
||||
of OptOut:
|
||||
if opts.ignore:
|
||||
debug "object field is opted out of deserialization ('ignore' is set), skipping"
|
||||
trace "object field is opted out of deserialization ('ignore' is set), skipping"
|
||||
skip = true
|
||||
elif hasDeserializePragma and opts.key == name:
|
||||
warn "object field marked as deserialize in OptOut mode, but 'ignore' not set, field will be deserialized"
|
||||
@ -214,7 +238,7 @@ proc fromJson*[T: ref object or object](_: type T, json: JsonNode): ?!T =
|
||||
if isOptionalValue:
|
||||
let jsonVal = json{opts.key}
|
||||
without parsed =? typeof(value).fromJson(jsonVal), e:
|
||||
debug "failed to deserialize field",
|
||||
trace "failed to deserialize field",
|
||||
`type` = $typeof(value), json = jsonVal, error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
@ -222,20 +246,111 @@ proc fromJson*[T: ref object or object](_: type T, json: JsonNode): ?!T =
|
||||
# not Option[T]
|
||||
elif opts.key in json and jsonVal =? json{opts.key}.catch and not jsonVal.isNil:
|
||||
without parsed =? typeof(value).fromJson(jsonVal), e:
|
||||
debug "failed to deserialize field",
|
||||
trace "failed to deserialize field",
|
||||
`type` = $typeof(value), json = jsonVal, error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
|
||||
success(res)
|
||||
|
||||
proc fromJson*[T: ref object or object](_: type T, bytes: seq[byte]): ?!T =
|
||||
let json = ?parse(string.fromBytes(bytes))
|
||||
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)
|
||||
T.fromJson(json)
|
||||
|
||||
proc fromJson*(_: type JsonNode, jsn: string): ?!JsonNode =
|
||||
return parser.parseJson(jsn)
|
||||
|
||||
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)
|
||||
|
||||
proc fromJson*[T: enum](_: type T, json: string): ?!T =
|
||||
T.fromJson(newJString(json))
|
||||
|
||||
proc fromJson*[T: SomeInteger or SomeFloat or openArray[byte] or bool](
|
||||
_: type T, json: string
|
||||
): ?!T =
|
||||
if json == "" or json == "null":
|
||||
let err = newSerdeError("Cannot deserialize '' or 'null' into type " & $T)
|
||||
failure err
|
||||
else:
|
||||
let jsn = ?JsonNode.parse(json)
|
||||
T.fromJson(jsn)
|
||||
|
||||
proc fromJson*[T: SomeInteger or SomeFloat or openArray[byte] or bool or enum](
|
||||
_: type Option[T], json: string
|
||||
): ?!Option[T] =
|
||||
if json == "" or json == "null":
|
||||
success T.none
|
||||
else:
|
||||
when T is enum:
|
||||
let jsn = newJString(json)
|
||||
else:
|
||||
let jsn = ?JsonNode.parse(json)
|
||||
Option[T].fromJson(jsn)
|
||||
|
||||
proc fromJson*[T: SomeInteger or SomeFloat or openArray[byte] or bool or enum](
|
||||
_: type seq[T], json: string
|
||||
): ?!seq[T] =
|
||||
if json == "" or json == "null":
|
||||
success newSeq[T]()
|
||||
else:
|
||||
if T is enum:
|
||||
let err = newSerdeError("Cannot deserialize a seq[enum]: not yet implemented, PRs welcome")
|
||||
return failure err
|
||||
|
||||
let jsn = ?JsonNode.parse(json)
|
||||
seq[T].fromJson(jsn)
|
||||
|
||||
proc fromJson*[T: SomeInteger or SomeFloat or openArray[byte] or bool or enum](
|
||||
_: type ?seq[T], json: string
|
||||
): ?!Option[seq[T]] =
|
||||
if json == "" or json == "null":
|
||||
success seq[T].none
|
||||
else:
|
||||
if T is enum:
|
||||
let err = newSerdeError("Cannot deserialize a seq[enum]: not yet implemented, PRs welcome")
|
||||
return failure err
|
||||
let jsn = ?JsonNode.parse(json)
|
||||
Option[seq[T]].fromJson(jsn)
|
||||
|
||||
proc fromJson*[T: ref object or object](_: type seq[T], json: string): ?!seq[T] =
|
||||
let jsn = ?JsonNode.parse(json) # full qualification required in-module only
|
||||
seq[T].fromJson(jsn)
|
||||
|
||||
proc fromJson*(T: typedesc[StUint or StInt], json: string): ?!T =
|
||||
T.fromJson(newJString(json))
|
||||
|
||||
proc fromJson*[T: ref object or object](_: type ?T, json: string): ?!Option[T] =
|
||||
when T is (StUint or StInt):
|
||||
let jsn = newJString(json)
|
||||
else:
|
||||
let jsn = ?JsonNode.parse(json) # full qualification required in-module only
|
||||
Option[T].fromJson(jsn)
|
||||
|
||||
# Force symbols into scope when mixins are used with generic overloads. When
|
||||
# mixins are used with generic overloads, the overloaded symbols in scope of the
|
||||
# mixin are evaluated from the perspective of the mixin. This creates issues in
|
||||
# downstream modules that may inadvertantly dispatch *only* to the symbols in
|
||||
# the scope of the mixin, even when the module with the wrong symbol overloads
|
||||
# is not imported. By forcing the compiler to use symbols for types handled by
|
||||
# serde, we can be sure that these symbols are available to downstream modules.
|
||||
# We can also be sure that these `fromJson` symbols can be overloaded where
|
||||
# needed.
|
||||
static:
|
||||
discard bool.fromJson("")
|
||||
discard Option[bool].fromJson("")
|
||||
discard seq[bool].fromJson("")
|
||||
discard uint.fromJson("")
|
||||
discard Option[uint].fromJson("")
|
||||
discard seq[uint].fromJson("")
|
||||
discard int.fromJson("")
|
||||
discard Option[int].fromJson("")
|
||||
discard seq[int].fromJson("")
|
||||
discard UInt256.fromJson("")
|
||||
discard Option[UInt256].fromJson("")
|
||||
discard seq[UInt256].fromJson("")
|
||||
discard JsonNode.fromJson("")
|
||||
type DistinctType = distinct int
|
||||
discard DistinctType.fromJson(newJString(""))
|
||||
discard UInt256.fromJson(newSeq[byte](0))
|
||||
|
||||
7
serde/json/helpers.nim
Normal file
7
serde/json/helpers.nim
Normal file
@ -0,0 +1,7 @@
|
||||
import std/json
|
||||
|
||||
func isEmptyString*(json: JsonNode): bool =
|
||||
return json.kind == JString and json.getStr == ""
|
||||
|
||||
func isNullString*(json: JsonNode): bool =
|
||||
return json.kind == JString and json.getStr == "null"
|
||||
@ -2,13 +2,17 @@ import std/json as stdjson
|
||||
|
||||
import pkg/questionable/results
|
||||
|
||||
import ./errors
|
||||
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
|
||||
without val =? stdjson.parseJson(json).catch, error:
|
||||
return failure error.mapErrTo(JsonParseError)
|
||||
return success val
|
||||
except Exception as e:
|
||||
return failure newException(JsonParseError, e.msg, e)
|
||||
|
||||
@ -21,30 +21,30 @@ export types
|
||||
{.push raises: [].}
|
||||
|
||||
logScope:
|
||||
topics = "json serialization"
|
||||
topics = "nimserde json serializer"
|
||||
|
||||
func `%`*(s: string): JsonNode =
|
||||
proc `%`*(s: string): JsonNode =
|
||||
newJString(s)
|
||||
|
||||
func `%`*(n: uint): JsonNode =
|
||||
proc `%`*(n: uint): JsonNode =
|
||||
if n > cast[uint](int.high):
|
||||
newJString($n)
|
||||
else:
|
||||
newJInt(BiggestInt(n))
|
||||
|
||||
func `%`*(n: int): JsonNode =
|
||||
proc `%`*(n: int): JsonNode =
|
||||
newJInt(n)
|
||||
|
||||
func `%`*(n: BiggestUInt): JsonNode =
|
||||
proc `%`*(n: BiggestUInt): JsonNode =
|
||||
if n > cast[BiggestUInt](BiggestInt.high):
|
||||
newJString($n)
|
||||
else:
|
||||
newJInt(BiggestInt(n))
|
||||
|
||||
func `%`*(n: BiggestInt): JsonNode =
|
||||
proc `%`*(n: BiggestInt): JsonNode =
|
||||
newJInt(n)
|
||||
|
||||
func `%`*(n: float): JsonNode =
|
||||
proc `%`*(n: float): JsonNode =
|
||||
if n != n:
|
||||
newJString("nan")
|
||||
elif n == Inf:
|
||||
@ -54,10 +54,10 @@ func `%`*(n: float): JsonNode =
|
||||
else:
|
||||
newJFloat(n)
|
||||
|
||||
func `%`*(b: bool): JsonNode =
|
||||
proc `%`*(b: bool): JsonNode =
|
||||
newJBool(b)
|
||||
|
||||
func `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
|
||||
proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
|
||||
if keyVals.len == 0:
|
||||
return newJArray()
|
||||
let jObj = newJObject()
|
||||
@ -68,13 +68,13 @@ func `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
|
||||
template `%`*(j: JsonNode): JsonNode =
|
||||
j
|
||||
|
||||
func `%`*[T](table: Table[string, T] | OrderedTable[string, T]): JsonNode =
|
||||
proc `%`*[T](table: Table[string, T] | OrderedTable[string, T]): JsonNode =
|
||||
let jObj = newJObject()
|
||||
for k, v in table:
|
||||
jObj[k] = ? %v
|
||||
jObj
|
||||
|
||||
func `%`*[T](opt: Option[T]): JsonNode =
|
||||
proc `%`*[T](opt: Option[T]): JsonNode =
|
||||
if opt.isSome:
|
||||
%(opt.get)
|
||||
else:
|
||||
@ -99,16 +99,20 @@ proc `%`*[T: object or ref object](obj: T): JsonNode =
|
||||
let hasSerialize = value.hasCustomPragma(serialize)
|
||||
var skip = false # workaround for 'continue' not supported in a 'fields' loop
|
||||
|
||||
# logScope moved into proc due to chronicles issue: https://github.com/status-im/nim-chronicles/issues/148
|
||||
logScope:
|
||||
topics = "serde json serialization"
|
||||
|
||||
case mode
|
||||
of OptIn:
|
||||
if not hasSerialize:
|
||||
debug "object field not marked with serialize, skipping"
|
||||
trace "object field not marked with serialize, skipping"
|
||||
skip = true
|
||||
elif opts.ignore:
|
||||
skip = true
|
||||
of OptOut:
|
||||
if opts.ignore:
|
||||
debug "object field opted out of serialization ('ignore' is set), skipping"
|
||||
trace "object field opted out of serialization ('ignore' is set), skipping"
|
||||
skip = true
|
||||
elif hasSerialize and opts.key == name: # all serialize params are default
|
||||
warn "object field marked as serialize in OptOut mode, but 'ignore' not set, field will be serialized"
|
||||
@ -125,22 +129,22 @@ proc `%`*[T: object or ref object](obj: T): JsonNode =
|
||||
proc `%`*(o: enum): JsonNode =
|
||||
% $o
|
||||
|
||||
func `%`*(stint: StInt | StUint): JsonNode =
|
||||
proc `%`*(stint: StInt | StUint): JsonNode =
|
||||
%stint.toString
|
||||
|
||||
func `%`*(cstr: cstring): JsonNode =
|
||||
proc `%`*(cstr: cstring): JsonNode =
|
||||
% $cstr
|
||||
|
||||
func `%`*(arr: openArray[byte]): JsonNode =
|
||||
proc `%`*(arr: openArray[byte]): JsonNode =
|
||||
%arr.to0xHex
|
||||
|
||||
func `%`*[T](elements: openArray[T]): JsonNode =
|
||||
proc `%`*[T](elements: openArray[T]): JsonNode =
|
||||
let jObj = newJArray()
|
||||
for elem in elements:
|
||||
jObj.add(%elem)
|
||||
jObj
|
||||
|
||||
func `%`*[T: distinct](id: T): JsonNode =
|
||||
proc `%`*[T: distinct](id: T): JsonNode =
|
||||
type baseType = T.distinctBase
|
||||
%baseType(id)
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import std/strutils
|
||||
|
||||
func flatten*(s: string): string =
|
||||
s.replace(" ")
|
||||
.replace("\n")
|
||||
s.replace(" ").replace("\n")
|
||||
|
||||
7
tests/json.nim
Normal file
7
tests/json.nim
Normal file
@ -0,0 +1,7 @@
|
||||
import ./json/testDeserialize
|
||||
import ./json/testDeserializeModes
|
||||
import ./json/testPragmas
|
||||
import ./json/testSerialize
|
||||
import ./json/testSerializeModes
|
||||
|
||||
{.warning[UnusedImport]: off.}
|
||||
209
tests/json/deserialize/objects.nim
Normal file
209
tests/json/deserialize/objects.nim
Normal file
@ -0,0 +1,209 @@
|
||||
import std/unittest
|
||||
import pkg/serde
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import pkg/stew/byteutils
|
||||
import pkg/stint
|
||||
|
||||
suite "json - deserialize objects":
|
||||
test "can deserialize json objects":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myint: int
|
||||
myoption: ?bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
||||
|
||||
let json =
|
||||
!JsonNode.parse(
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myint": 123,
|
||||
"myoption": true
|
||||
}"""
|
||||
)
|
||||
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "ignores serialize pragma when deserializing":
|
||||
type MyObj = object
|
||||
mystring {.serialize.}: string
|
||||
mybool: bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", mybool: true)
|
||||
|
||||
let json =
|
||||
!JsonNode.parse(
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true
|
||||
}"""
|
||||
)
|
||||
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes objects with extra fields":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
mybool: bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", mybool: true)
|
||||
|
||||
let json =
|
||||
!JsonNode.parse(
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true,
|
||||
"extra": "extra"
|
||||
}"""
|
||||
)
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes objects with less fields":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
mybool: bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", mybool: false)
|
||||
|
||||
let json =
|
||||
!JsonNode.parse(
|
||||
"""{
|
||||
"mystring": "abc"
|
||||
}"""
|
||||
)
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes ref objects":
|
||||
type MyRef = ref object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = MyRef(mystring: "abc", myint: 1)
|
||||
|
||||
let json =
|
||||
!JsonNode.parse(
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}"""
|
||||
)
|
||||
|
||||
let deserialized = !MyRef.fromJson(json)
|
||||
check deserialized.mystring == expected.mystring
|
||||
check deserialized.myint == expected.myint
|
||||
|
||||
test "deserializes openArray[byte]":
|
||||
type MyRef = ref object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = MyRef(mystring: "abc", myint: 1)
|
||||
let byteArray = """{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}""".toBytes
|
||||
|
||||
let deserialized = !MyRef.fromJson(byteArray)
|
||||
check deserialized.mystring == expected.mystring
|
||||
check deserialized.myint == expected.myint
|
||||
|
||||
suite "json - deserialize objects from string":
|
||||
test "deserializes objects from string":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = MyObj(mystring: "abc", myint: 1)
|
||||
let myObjJson =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}"""
|
||||
|
||||
check !MyObj.fromJson(myObjJson) == expected
|
||||
|
||||
test "deserializes ref objects from string":
|
||||
type MyRef = ref object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = MyRef(mystring: "abc", myint: 1)
|
||||
let myRefJson =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}"""
|
||||
|
||||
let deserialized = !MyRef.fromJson(myRefJson)
|
||||
check deserialized.mystring == expected.mystring
|
||||
check deserialized.myint == expected.myint
|
||||
|
||||
test "deserializes seq of objects from string":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = @[MyObj(mystring: "abc", myint: 1)]
|
||||
let myObjsJson =
|
||||
"""[{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}]"""
|
||||
|
||||
check !seq[MyObj].fromJson(myObjsJson) == expected
|
||||
|
||||
test "deserializes Option of object from string":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = some MyObj(mystring: "abc", myint: 1)
|
||||
let myObjJson =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}"""
|
||||
|
||||
check !(Option[MyObj].fromJson(myObjJson)) == expected
|
||||
|
||||
test "deserializes object with UInt256 from string":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myu256: UInt256
|
||||
|
||||
let expected = MyObj(mystring: "abc", myu256: 1.u256)
|
||||
let myObjJson =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myu256": 1
|
||||
}"""
|
||||
|
||||
check !MyObj.fromJson(myObjJson) == expected
|
||||
|
||||
test "deserializes object with stringified UInt256 from string":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myu256: UInt256
|
||||
|
||||
let expected = MyObj(mystring: "abc", myu256: 1.u256)
|
||||
let myObjJson =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myu256": "1"
|
||||
}"""
|
||||
|
||||
check !MyObj.fromJson(myObjJson) == expected
|
||||
|
||||
test "deserializes object with ?UInt256 from string":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myu256: ?UInt256
|
||||
|
||||
let expected = MyObj(mystring: "abc", myu256: UInt256.none)
|
||||
let myObjJson =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
}"""
|
||||
|
||||
check !MyObj.fromJson(myObjJson) == expected
|
||||
217
tests/json/deserialize/std.nim
Normal file
217
tests/json/deserialize/std.nim
Normal file
@ -0,0 +1,217 @@
|
||||
import std/math
|
||||
import std/unittest
|
||||
import pkg/serde
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
|
||||
suite "json - deserialize std types":
|
||||
test "deserializes NaN float":
|
||||
check %NaN == newJString("nan")
|
||||
|
||||
test "deserialize enum":
|
||||
type MyEnum = enum
|
||||
First
|
||||
Second
|
||||
|
||||
let json = newJString("Second")
|
||||
check !MyEnum.fromJson(json) == Second
|
||||
|
||||
test "deserializes Option[T] when has a value":
|
||||
let json = newJInt(1)
|
||||
check (!fromJson(?int, json) == some 1)
|
||||
|
||||
test "deserializes Option[T] when has a string value":
|
||||
check (!fromJson(?int, "1") == some 1)
|
||||
|
||||
test "deserializes Option[T] from empty string":
|
||||
check (!fromJson(?int, "") == int.none)
|
||||
|
||||
test "deserializes Option[T] from empty string":
|
||||
check (!fromJson(?int, "") == int.none)
|
||||
|
||||
test "cannot deserialize T from null string":
|
||||
let res = fromJson(int, "null")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize '' or 'null' into type int"
|
||||
|
||||
test "deserializes Option[T] when doesn't have a value":
|
||||
let json = newJNull()
|
||||
check !fromJson(?int, json) == none int
|
||||
|
||||
test "deserializes float":
|
||||
let json = newJFloat(1.234)
|
||||
check !float.fromJson(json) == 1.234
|
||||
|
||||
test "deserializes float from string":
|
||||
check !float.fromJson("1.234") == 1.234
|
||||
|
||||
test "cannot deserialize float from empty string":
|
||||
let res = float.fromJson("")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize '' or 'null' into type float"
|
||||
|
||||
test "cannot deserialize float from null string":
|
||||
let res = float.fromJson("null")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize '' or 'null' into type float"
|
||||
|
||||
test "deserializes Inf float":
|
||||
let json = newJString("inf")
|
||||
check !float.fromJson(json) == Inf
|
||||
|
||||
test "deserializes -Inf float":
|
||||
let json = newJString("-inf")
|
||||
check !float.fromJson(json) == -Inf
|
||||
|
||||
test "deserializes NaN float":
|
||||
let json = newJString("nan")
|
||||
check (!float.fromJson(json)).isNaN
|
||||
|
||||
test "deserializes array to sequence":
|
||||
let expected = @[1, 2, 3]
|
||||
let json = !JsonNode.parse("[1,2,3]")
|
||||
check !seq[int].fromJson(json) == expected
|
||||
|
||||
test "deserializes uints int.high or smaller":
|
||||
let largeUInt: uint = uint(int.high)
|
||||
let json = newJInt(BiggestInt(largeUInt))
|
||||
check !uint.fromJson(json) == largeUInt
|
||||
|
||||
test "deserializes large uints":
|
||||
let largeUInt: uint = uint(int.high) + 1'u
|
||||
let json = newJString($BiggestUInt(largeUInt))
|
||||
check !uint.fromJson(json) == largeUInt
|
||||
|
||||
test "deserializes bool from JBool":
|
||||
let json = newJBool(true)
|
||||
check !bool.fromJson(json)
|
||||
|
||||
test "deserializes bool from string":
|
||||
check !bool.fromJson("true")
|
||||
|
||||
test "cannot deserialize bool from empty string":
|
||||
let res = bool.fromJson("")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize '' or 'null' into type bool"
|
||||
|
||||
test "cannot deserialize bool from null string":
|
||||
let res = bool.fromJson("null")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize '' or 'null' into type bool"
|
||||
|
||||
test "deserializes ?bool from string":
|
||||
check Option[bool].fromJson("true") == success true.some
|
||||
|
||||
test "deserializes ?bool from empty string":
|
||||
check !Option[bool].fromJson("") == bool.none
|
||||
|
||||
test "deserializes ?bool from null string":
|
||||
check !Option[bool].fromJson("null") == bool.none
|
||||
|
||||
test "deserializes seq[bool] from JArray":
|
||||
let json = newJArray()
|
||||
json.add(newJBool(true))
|
||||
json.add(newJBool(false))
|
||||
check !seq[bool].fromJson(json) == @[true, false]
|
||||
|
||||
test "deserializes seq[bool] from string":
|
||||
check !seq[bool].fromJson("[true, false]") == @[true, false]
|
||||
|
||||
test "deserializes seq[bool] from empty string":
|
||||
check !seq[bool].fromJson("") == newSeq[bool]()
|
||||
|
||||
test "deserializes seq[bool] from null string":
|
||||
check !seq[bool].fromJson("null") == newSeq[bool]()
|
||||
|
||||
test "cannot deserialize seq[bool] from unknown string":
|
||||
let res = seq[bool].fromJson("blah")
|
||||
check res.error of JsonParseError
|
||||
check res.error.msg == "input(1, 4) Error: { expected"
|
||||
|
||||
test "deserializes ?seq[bool] from string":
|
||||
check Option[seq[bool]].fromJson("[true, false]") == success @[true, false].some
|
||||
|
||||
test "deserializes ?seq[bool] from empty string":
|
||||
check !Option[seq[bool]].fromJson("") == seq[bool].none
|
||||
|
||||
test "deserializes ?seq[bool] from null string":
|
||||
check !Option[seq[bool]].fromJson("null") == seq[bool].none
|
||||
|
||||
test "deserializes enum from JString":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
let json = newJString("one")
|
||||
check !MyEnum.fromJson(json) == MyEnum.one
|
||||
|
||||
test "deserializes enum from string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
check !MyEnum.fromJson("one") == MyEnum.one
|
||||
|
||||
test "cannot deserialize enum from empty string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
let res = MyEnum.fromJson("")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Invalid enum value: "
|
||||
|
||||
test "cannot deserialize enum from null string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
let res = MyEnum.fromJson("null")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Invalid enum value: null"
|
||||
|
||||
test "deserializes ?enum from string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
check Option[MyEnum].fromJson("one") == success MyEnum.one.some
|
||||
|
||||
test "deserializes ?enum from empty string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
check !Option[MyEnum].fromJson("") == MyEnum.none
|
||||
|
||||
test "deserializes ?enum from null string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
check !Option[MyEnum].fromJson("null") == MyEnum.none
|
||||
|
||||
test "deserializes seq[enum] from string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
two
|
||||
|
||||
let res = seq[MyEnum].fromJson("[one,two]")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg ==
|
||||
"Cannot deserialize a seq[enum]: not yet implemented, PRs welcome"
|
||||
|
||||
test "deserializes ?seq[enum] from string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
two
|
||||
|
||||
let res = Option[seq[MyEnum]].fromJson("[one,two]")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg ==
|
||||
"Cannot deserialize a seq[enum]: not yet implemented, PRs welcome"
|
||||
|
||||
test "deserializes ?seq[MyEnum] from empty string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
check !Option[seq[MyEnum]].fromJson("") == seq[MyEnum].none
|
||||
|
||||
test "deserializes ?seq[MyEnum] from null string":
|
||||
type MyEnum = enum
|
||||
one
|
||||
|
||||
check !Option[seq[MyEnum]].fromJson("null") == seq[MyEnum].none
|
||||
122
tests/json/deserialize/stint.nim
Normal file
122
tests/json/deserialize/stint.nim
Normal file
@ -0,0 +1,122 @@
|
||||
import std/options
|
||||
import std/unittest
|
||||
import pkg/stint
|
||||
import pkg/serde
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
|
||||
suite "json - deserialize stint":
|
||||
test "deserializes UInt256 from an empty JString":
|
||||
let json = newJString("")
|
||||
check !UInt256.fromJson(json) == 0.u256
|
||||
|
||||
test "deserializes UInt256 from an empty string":
|
||||
check !UInt256.fromJson("") == 0.u256
|
||||
|
||||
test "deserializes UInt256 from null string":
|
||||
let res = UInt256.fromJson("null")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize 'null' into type UInt256"
|
||||
|
||||
test "deserializes UInt256 from JNull":
|
||||
let res = UInt256.fromJson(newJNull())
|
||||
check res.error of UnexpectedKindError
|
||||
check res.error.msg ==
|
||||
"deserialization to UInt256 failed: expected {JInt, JString} but got JNull"
|
||||
|
||||
test "deserializes ?UInt256 from an empty JString":
|
||||
let json = newJString("")
|
||||
check !Option[UInt256].fromJson(json) == UInt256.none
|
||||
|
||||
test "deserializes ?UInt256 from an empty string":
|
||||
check !Option[UInt256].fromJson("") == UInt256.none
|
||||
|
||||
test "deserializes ?UInt256 from null string":
|
||||
check !Option[UInt256].fromJson("null") == UInt256.none
|
||||
|
||||
test "deserializes ?UInt256 from JNull":
|
||||
check !Option[UInt256].fromJson(newJNull()) == UInt256.none
|
||||
|
||||
test "deserializes seq[UInt256] from string":
|
||||
check seq[UInt256].fromJson("[1,2,3]") == success @[1.u256, 2.u256, 3.u256]
|
||||
|
||||
test "deserializes seq[UInt256] from string with empty string item":
|
||||
check seq[UInt256].fromJson("[1,2,\"\"]") == success @[1.u256, 2.u256, 0.u256]
|
||||
|
||||
test "deserializes seq[UInt256] from string with null item":
|
||||
let res = seq[UInt256].fromJson("[1,2,null]")
|
||||
check res.error of UnexpectedKindError
|
||||
check res.error.msg ==
|
||||
"deserialization to UInt256 failed: expected {JInt, JString} but got JNull"
|
||||
|
||||
test "deserializes seq[UInt256] from string with null string item":
|
||||
let res = seq[UInt256].fromJson("[1,2,\"null\"]")
|
||||
check res.error of SerdeError
|
||||
check res.error.msg == "Cannot deserialize 'null' into type UInt256"
|
||||
|
||||
test "deserializes seq[?UInt256] from string":
|
||||
check seq[?UInt256].fromJson("[1,2,3]") ==
|
||||
success @[1.u256.some, 2.u256.some, 3.u256.some]
|
||||
|
||||
test "deserializes seq[?UInt256] from string with empty string item":
|
||||
check seq[?UInt256].fromJson("[1,2,\"\"]") ==
|
||||
success @[1.u256.some, 2.u256.some, UInt256.none]
|
||||
|
||||
test "deserializes seq[?UInt256] from string with null item":
|
||||
check seq[?UInt256].fromJson("[1,2,null]") ==
|
||||
success @[1.u256.some, 2.u256.some, UInt256.none]
|
||||
|
||||
test "deserializes seq[?UInt256] from string with null string item":
|
||||
check seq[?UInt256].fromJson("[1,2,\"null\"]") ==
|
||||
success @[1.u256.some, 2.u256.some, UInt256.none]
|
||||
|
||||
test "deserializes UInt256 from JString with no prefix":
|
||||
let json = newJString("1")
|
||||
check !UInt256.fromJson(json) == 1.u256
|
||||
|
||||
test "deserializes ?UInt256 from JString with no prefix":
|
||||
let json = newJString("1")
|
||||
check !Option[UInt256].fromJson(json) == 1.u256.some
|
||||
|
||||
test "deserializes UInt256 from string with no prefix":
|
||||
check !UInt256.fromJson("1") == 1.u256
|
||||
|
||||
test "deserializes ?UInt256 from string with no prefix":
|
||||
check !Option[UInt256].fromJson("1") == 1.u256.some
|
||||
|
||||
test "deserializes UInt256 from hex JString representation":
|
||||
let json = newJString("0x1")
|
||||
check !UInt256.fromJson(json) == 0x1.u256
|
||||
|
||||
test "deserializes ?UInt256 from hex JString representation":
|
||||
let json = newJString("0x1")
|
||||
check !Option[UInt256].fromJson(json) == 0x1.u256.some
|
||||
|
||||
test "deserializes ?UInt256 from hex string representation":
|
||||
check !Option[UInt256].fromJson("0x1") == 0x1.u256.some
|
||||
|
||||
test "deserializes UInt256 from octal JString representation":
|
||||
let json = newJString("0o1")
|
||||
check !UInt256.fromJson(json) == 0o1.u256
|
||||
|
||||
test "deserializes ?UInt256 from octal JString representation":
|
||||
let json = newJString("0o1")
|
||||
check !Option[UInt256].fromJson(json) == 0o1.u256.some
|
||||
|
||||
test "deserializes ?UInt256 from octal string representation":
|
||||
check !Option[UInt256].fromJson("0o1") == 0o1.u256.some
|
||||
|
||||
test "deserializes UInt256 from binary JString representation":
|
||||
let json = newJString("0b1")
|
||||
check !UInt256.fromJson(json) == 0b1.u256
|
||||
|
||||
test "deserializes ?UInt256 from binary JString representation":
|
||||
let json = newJString("0b1")
|
||||
check !Option[UInt256].fromJson(json) == 0b1.u256.some
|
||||
|
||||
test "deserializes ?UInt256 from binary string representation":
|
||||
check !Option[UInt256].fromJson("0b1") == 0b1.u256.some
|
||||
|
||||
test "deserializes Int256 with no prefix":
|
||||
let json = newJString("1")
|
||||
check !Int256.fromJson(json) == 1.i256
|
||||
@ -1,139 +1,3 @@
|
||||
import std/math
|
||||
import std/options
|
||||
import std/unittest
|
||||
import pkg/stint
|
||||
import pkg/serde
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
|
||||
suite "json serialization - deserialize":
|
||||
test "deserializes NaN float":
|
||||
check %NaN == newJString("nan")
|
||||
|
||||
test "deserialize enum":
|
||||
type MyEnum = enum
|
||||
First
|
||||
Second
|
||||
|
||||
let json = newJString("Second")
|
||||
check !MyEnum.fromJson(json) == Second
|
||||
|
||||
test "deserializes UInt256 from non-hex string representation":
|
||||
let json = newJString("100000")
|
||||
check !UInt256.fromJson(json) == 100000.u256
|
||||
|
||||
test "deserializes Option[T] when has a value":
|
||||
let json = newJInt(1)
|
||||
check (!fromJson(?int, json) == some 1)
|
||||
|
||||
test "deserializes Option[T] when doesn't have a value":
|
||||
let json = newJNull()
|
||||
check !fromJson(?int, json) == none int
|
||||
|
||||
test "deserializes float":
|
||||
let json = newJFloat(1.234)
|
||||
check !float.fromJson(json) == 1.234
|
||||
|
||||
test "deserializes Inf float":
|
||||
let json = newJString("inf")
|
||||
check !float.fromJson(json) == Inf
|
||||
|
||||
test "deserializes -Inf float":
|
||||
let json = newJString("-inf")
|
||||
check !float.fromJson(json) == -Inf
|
||||
|
||||
test "deserializes NaN float":
|
||||
let json = newJString("nan")
|
||||
check (!float.fromJson(json)).isNaN
|
||||
|
||||
test "deserializes array to sequence":
|
||||
let expected = @[1, 2, 3]
|
||||
let json = !"[1,2,3]".parseJson
|
||||
check !seq[int].fromJson(json) == expected
|
||||
|
||||
test "deserializes uints int.high or smaller":
|
||||
let largeUInt: uint = uint(int.high)
|
||||
let json = newJInt(BiggestInt(largeUInt))
|
||||
check !uint.fromJson(json) == largeUInt
|
||||
|
||||
test "deserializes large uints":
|
||||
let largeUInt: uint = uint(int.high) + 1'u
|
||||
let json = newJString($BiggestUInt(largeUInt))
|
||||
check !uint.fromJson(json) == largeUInt
|
||||
|
||||
test "can deserialize json objects":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
myint: int
|
||||
myoption: ?bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
||||
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"myint": 123,
|
||||
"myoption": true
|
||||
}""".parseJson
|
||||
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "ignores serialize pragma when deserializing":
|
||||
type MyObj = object
|
||||
mystring {.serialize.}: string
|
||||
mybool: bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", mybool: true)
|
||||
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true
|
||||
}""".parseJson
|
||||
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes objects with extra fields":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
mybool: bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", mybool: true)
|
||||
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true,
|
||||
"extra": "extra"
|
||||
}""".parseJson
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes objects with less fields":
|
||||
type MyObj = object
|
||||
mystring: string
|
||||
mybool: bool
|
||||
|
||||
let expected = MyObj(mystring: "abc", mybool: false)
|
||||
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc"
|
||||
}""".parseJson
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes ref objects":
|
||||
type MyRef = ref object
|
||||
mystring: string
|
||||
myint: int
|
||||
|
||||
let expected = MyRef(mystring: "abc", myint: 1)
|
||||
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}""".parseJson
|
||||
|
||||
let deserialized = !MyRef.fromJson(json)
|
||||
check deserialized.mystring == expected.mystring
|
||||
check deserialized.myint == expected.myint
|
||||
import ./deserialize/objects
|
||||
import ./deserialize/std
|
||||
import ./deserialize/stint
|
||||
|
||||
@ -4,4 +4,4 @@ import ./json/testPragmas
|
||||
import ./json/testSerialize
|
||||
import ./json/testSerializeModes
|
||||
|
||||
{.warning[UnusedImport]:off.}
|
||||
{.warning[UnusedImport]: off.}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
version = "0.1.0"
|
||||
author = "nim serde authors"
|
||||
description = "tests for nim serde library"
|
||||
license = "MIT"
|
||||
|
||||
requires "asynctest >= 0.5.1 & < 0.6.0"
|
||||
requires "questionable >= 0.10.13 & < 0.11.0"
|
||||
|
||||
task test, "Run the test suite":
|
||||
exec "nimble install -d -y"
|
||||
exec "nim c -r test"
|
||||
Loading…
x
Reference in New Issue
Block a user