Compare commits

...

44 Commits

Author SHA1 Message Date
codex-storage[bot]
649ae60e05
docs: update CHANGELOG.md for master [skip ci] 2025-12-16 06:10:17 +00:00
Arnaud
ca4457a8b0
chore: rename Codex to Logos Storage (#34)
Rename Codex to Logos Storage
2025-12-16 07:09:50 +01:00
codex-storage[bot]
1c0f313662
docs: update CHANGELOG.md for master [skip ci] 2025-10-28 22:24:05 +00:00
markspanbroek
60fcbdab3c
Update chronicles (#33)
* chore: allow for newer versions of chronicles

Signed-off-by: Mark Spanbroek <mark@spanbroek.net>

* chore: add nimble lock

Signed-off-by: Mark Spanbroek <mark@spanbroek.net>

* chore!: no longer support nim 1.6.x

Signed-off-by: Mark Spanbroek <mark@spanbroek.net>

* fix: make tests work with nimble lock file

Signed-off-by: Mark Spanbroek <mark@spanbroek.net>

---------

Signed-off-by: Mark Spanbroek <mark@spanbroek.net>
2025-10-29 09:23:36 +11:00
codex-storage[bot]
5ced7c88b9
docs: update CHANGELOG.md for master [skip ci] 2025-02-19 23:31:17 +00:00
Dmitriy Ryajov
1ce89b206e
fix loglevel and add log scope to allow filtering and avoid log pollution (#30)
fix loglevel and add log scope to avoid log pollution
2025-02-20 10:30:47 +11:00
codex-storage[bot]
69a7a0111a
docs: update CHANGELOG.md for master [skip ci] 2024-10-25 06:54:10 +00:00
Slava
7f9622fa5c
ci: add matrix status job (#29)
adds matrix status job to decouple branch rules status check from a jobs names which are based on Nim version
2024-10-25 17:53:40 +11:00
codex-storage[bot]
fc4ef51b81
docs: update CHANGELOG.md for v1.2.3 [skip ci] 2024-10-23 06:31:21 +00:00
codex-storage[bot]
83e4a2ccf6
docs: update CHANGELOG.md for master [skip ci] 2024-10-23 06:20:37 +00:00
Eric
b2f4dbcd25
v1.2.2 (#28) 2024-10-23 17:20:04 +11:00
codex-storage[bot]
f5af686a85
docs: update CHANGELOG.md for master [skip ci] 2024-10-23 05:53:57 +00:00
Eric
69c630212c
chore: bumps nim from 1.6.16 to 1.6.20 in ci (#27)
bumps nim from 1.6.16 to 1.6.20 in ci
2024-10-23 16:53:28 +11:00
codex-storage[bot]
9ec0e0d5a6
docs: update CHANGELOG.md for master [skip ci] 2024-10-23 05:33:08 +00:00
Eric
c81b751602
chore: remove unneeded echos (#26)
Remove unneeded echos
2024-10-23 16:32:41 +11:00
codex-storage[bot]
cf59b42ed5
docs: update CHANGELOG.md for v1.2.1 [skip ci] 2024-05-21 02:47:23 +00:00
codex-storage[bot]
b39497be32
docs: update CHANGELOG.md for master [skip ci] 2024-05-21 02:46:16 +00:00
Eric
71f30d4678
v1.2.1 (#25) 2024-05-21 12:45:48 +10:00
codex-storage[bot]
6bf626cab4
docs: update CHANGELOG.md for master [skip ci] 2024-05-21 02:40:12 +00:00
Eric
cdba47becf
fix: force symbol resolution for types that serde de/serializes (#24)
* fix: force symbol resolution for types that serde de/serializes

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.

* remove enum forced scoping

Forcing a scoping for a particular enum type would only resolve that type and not all enum types.

* Add mixin + generic overloads as known issue to README

* try to fix URL reference to deserializer.nim
2024-05-21 12:39:47 +10:00
codex-storage[bot]
7fb79610c4
docs: update CHANGELOG.md for master [skip ci] 2024-05-16 07:58:06 +00:00
Eric
10271bd494
feat: improve deserialization from string (#23)
Improves how types handle deserialization from string, including Option[T], seq[T], and Option[seq[T]]
Empty and null strings are no longer deserialized to 0, instead an error Result is returned
If an error occurs when parsing json, a JsonParseError is returned.
2024-05-16 17:57:42 +10:00
codex-storage[bot]
9dd16685d1
docs: update CHANGELOG.md for master [skip ci] 2024-05-16 05:09:36 +00:00
Eric
6d2fc9406a
feat: improve stint parsing (#22)
- change empty string value to none when optional
- handle null, "null", JNull, seq[stint], seq[?string]
2024-05-16 15:09:01 +10:00
codex-storage[bot]
d84641333a
docs: update CHANGELOG.md for v1.2.0 [skip ci] 2024-05-14 04:56:11 +00:00
codex-storage[bot]
d726f6c6eb
docs: update CHANGELOG.md for master [skip ci] 2024-05-14 04:55:06 +00:00
Eric
b7995afb35
chore: v1.2.0 (#21) 2024-05-14 14:54:37 +10:00
codex-storage[bot]
34e28c33d9
docs: update CHANGELOG.md for master [skip ci] 2024-05-14 04:53:00 +00:00
Eric
bd262054f2
fix: add missing test update (#20) 2024-05-14 14:52:32 +10:00
codex-storage[bot]
f9f4b3b662
docs: update CHANGELOG.md for master [skip ci] 2024-05-14 04:49:43 +00:00
Eric
2fcdc9e9c5
chore: reorganize deserialize tests (#19)
Distribute deserialization tests so they are organized up by type.
2024-05-14 14:49:15 +10:00
codex-storage[bot]
2c0518bed0
docs: update CHANGELOG.md for master [skip ci] 2024-05-14 04:46:08 +00:00
Eric
3957da5b52
fix: UInt256 not correctly deserializing from string (#18)
fix: UInt256 not correctly deserialzing from string

UInt256 was being deserialized as an object when being deserialized from a string (not a JString).
2024-05-14 14:45:42 +10:00
codex-storage[bot]
1cedad7488
docs: update CHANGELOG.md for v1.1.1 [skip ci] 2024-05-14 04:29:09 +00:00
codex-storage[bot]
b955985548
docs: update CHANGELOG.md for master [skip ci] 2024-05-13 05:16:15 +00:00
Eric
414a23b52f
chore: v.1.1.1 (#17) 2024-05-13 15:15:46 +10:00
codex-storage[bot]
419d48c8d3
docs: update CHANGELOG.md for master [skip ci] 2024-05-13 05:09:58 +00:00
Eric
baa847e937
chore[formatting]: update formatting (#16)
update formatting
2024-05-13 15:09:34 +10:00
codex-storage[bot]
cefbc47367
docs: update CHANGELOG.md for master [skip ci] 2024-05-13 05:07:31 +00:00
Eric
fbabe954b4
add empty string test for UInt256 (#15) 2024-05-13 15:07:00 +10:00
codex-storage[bot]
b1e5e5d39a
docs: update CHANGELOG.md for master [skip ci] 2024-04-26 08:25:35 +00:00
Ben Bierens
a4a6b0d949
Fix log topics (#14)
adds serde log topics

moves `logScope` into routine scope due to a bug in chronicles
2024-04-26 18:25:02 +10:00
codex-storage[bot]
0b29da6e98
docs: update CHANGELOG.md for master [skip ci] 2024-02-14 06:25:40 +00:00
Eric
6e79374c87
run changelog workflow on release (#12) 2024-02-14 17:25:18 +11:00
21 changed files with 967 additions and 329 deletions

View File

@ -4,6 +4,8 @@ on:
push:
branches:
- master
release:
types: [published]
jobs:

View File

@ -12,17 +12,25 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
nim: [1.6.16, stable]
nim: [2.0.16, 2.2.4]
steps:
- name: Checkout
uses: actions/checkout@v4
- 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: Install Nim
uses: iffy/install-nim@v4
with:
version: ${{ matrix.nim }}
- name: Build
run: nimble install -y
- name: Test
run: nimble test -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
View File

@ -5,4 +5,5 @@ nimble.develop
nimble.paths
.idea
vendor/
.vscode/
.vscode/
nimbledeps

View File

@ -1,40 +1,95 @@
# Changelog
## [Unreleased](https://github.com/codex-storage/nim-serde/tree/HEAD)
## [Unreleased](https://github.com/logos-storage/nim-serde/tree/HEAD)
[Full Changelog](https://github.com/codex-storage/nim-serde/compare/v1.0.0...HEAD)
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v.1.2.2...HEAD)
**Merged pull requests:**
- chore: v1.1.0 [\#11](https://github.com/codex-storage/nim-serde/pull/11) ([emizzle](https://github.com/emizzle))
- deserialize non-prefixed stuint [\#10](https://github.com/codex-storage/nim-serde/pull/10) ([emizzle](https://github.com/emizzle))
- deserialize seq\[T\] and Option\[T\] from string [\#9](https://github.com/codex-storage/nim-serde/pull/9) ([emizzle](https://github.com/emizzle))
- 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))
## [v1.0.0](https://github.com/codex-storage/nim-serde/tree/v1.0.0) (2024-02-13)
## [v.1.2.2](https://github.com/logos-storage/nim-serde/tree/v.1.2.2) (2024-10-23)
[Full Changelog](https://github.com/codex-storage/nim-serde/compare/v0.1.2...v1.0.0)
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.2.1...v.1.2.2)
**Merged pull requests:**
- v1.0.0 [\#8](https://github.com/codex-storage/nim-serde/pull/8) ([emizzle](https://github.com/emizzle))
- fix: change serializer funcs to procs [\#7](https://github.com/codex-storage/nim-serde/pull/7) ([emizzle](https://github.com/emizzle))
- Use token for changelog generator [\#6](https://github.com/codex-storage/nim-serde/pull/6) ([veaceslavdoina](https://github.com/veaceslavdoina))
- Adjust workflows for changelog generation [\#5](https://github.com/codex-storage/nim-serde/pull/5) ([veaceslavdoina](https://github.com/veaceslavdoina))
- Change parseJson to JsonNode.parse [\#4](https://github.com/codex-storage/nim-serde/pull/4) ([emizzle](https://github.com/emizzle))
- Add CI workflow [\#3](https://github.com/codex-storage/nim-serde/pull/3) ([emizzle](https://github.com/emizzle))
- Fix deserialization of openArray\[byte\] [\#2](https://github.com/codex-storage/nim-serde/pull/2) ([emizzle](https://github.com/emizzle))
- 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))
## [v0.1.2](https://github.com/codex-storage/nim-serde/tree/v0.1.2) (2024-02-08)
## [v1.2.1](https://github.com/logos-storage/nim-serde/tree/v1.2.1) (2024-05-21)
[Full Changelog](https://github.com/codex-storage/nim-serde/compare/v0.1.1...v0.1.2)
[Full Changelog](https://github.com/logos-storage/nim-serde/compare/v1.2.0...v1.2.1)
## [v0.1.1](https://github.com/codex-storage/nim-serde/tree/v0.1.1) (2024-02-07)
**Merged pull requests:**
[Full Changelog](https://github.com/codex-storage/nim-serde/compare/v0.1.0...v0.1.1)
- 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))
## [v0.1.0](https://github.com/codex-storage/nim-serde/tree/v0.1.0) (2024-02-07)
## [v1.2.0](https://github.com/logos-storage/nim-serde/tree/v1.2.0) (2024-05-14)
[Full Changelog](https://github.com/codex-storage/nim-serde/compare/5a8e85449d9320d2277bc9aadf1daae61c7f057b...v0.1.0)
[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)

View File

@ -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

View File

@ -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):
@ -388,3 +388,25 @@ proc parseMe(me: string): JsonNode {.raises: [MyAppError].} =
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).

View File

@ -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
View 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": {}
}

View File

@ -1,20 +1,14 @@
# Package
version = "1.1.0"
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"

View File

@ -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)
@ -127,22 +134,34 @@ proc fromJson*[T: distinct](_: type T, json: JsonNode): ?!T =
success T(?T.distinctBase.fromJson(json))
proc fromJson*(T: typedesc[StUint or StInt], json: JsonNode): ?!T =
expectJsonKind(T, JString, json)
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)
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)
@ -188,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:
@ -197,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"
@ -215,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
@ -223,7 +246,7 @@ 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
@ -241,10 +264,93 @@ 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] =
let jsn = ?JsonNode.parse(json) # full qualification required in-module only
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
View 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"

View File

@ -2,6 +2,7 @@ import std/json as stdjson
import pkg/questionable/results
import ./errors
import ./types
{.push raises: [].}
@ -10,6 +11,8 @@ 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)

View File

@ -21,7 +21,7 @@ export types
{.push raises: [].}
logScope:
topics = "json serialization"
topics = "nimserde json serializer"
proc `%`*(s: string): JsonNode =
newJString(s)
@ -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"

View File

@ -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
View File

@ -0,0 +1,7 @@
import ./json/testDeserialize
import ./json/testDeserializeModes
import ./json/testPragmas
import ./json/testSerialize
import ./json/testSerializeModes
{.warning[UnusedImport]: off.}

View 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

View 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

View 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

View File

@ -1,241 +1,3 @@
import std/math
import std/options
import std/unittest
import pkg/stint
import pkg/serde
import pkg/stew/byteutils
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 with no prefix":
let json = newJString("1")
check !UInt256.fromJson(json) == 1.u256
test "deserializes UInt256 from hex string representation":
let json = newJString("0x1")
check !UInt256.fromJson(json) == 0x1.u256
test "deserializes UInt256 from octal string representation":
let json = newJString("0o1")
check !UInt256.fromJson(json) == 0o1.u256
test "deserializes UInt256 from binary string representation":
let json = newJString("0b1")
check !UInt256.fromJson(json) == 0b1.u256
test "deserializes UInt256 from non-hex string representation":
let json = newJString("100000")
check !UInt256.fromJson(json) == 100000.u256
test "deserializes Int256 with no prefix":
let json = newJString("1")
check !Int256.fromJson(json) == 1.i256
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 = !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 "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 "deserialize 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[T] 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[T] 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
import ./deserialize/objects
import ./deserialize/std
import ./deserialize/stint

View File

@ -4,4 +4,4 @@ import ./json/testPragmas
import ./json/testSerialize
import ./json/testSerializeModes
{.warning[UnusedImport]:off.}
{.warning[UnusedImport]: off.}

View File

@ -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"