-
Notifications
You must be signed in to change notification settings - Fork 3
/
jsons.nim
245 lines (198 loc) · 5.3 KB
/
jsons.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
## Convert to and from json with nulls and missing fileds
import json, strutils
proc notNil(x: SomeInteger|SomeFloat|string|bool|object|seq|enum): bool =
true
proc notNil(x: ref object|cstring): bool =
x != nil
proc toJson*(x: SomeInteger|SomeFloat|string|bool): JsonNode =
%x
proc toJson*(x: cstring): JsonNode =
%($x)
proc toJson*[T](x: openArray[T]): JsonNode =
result = newJArray()
for value in x:
result.add(value.toJson())
proc toJson*(x: enum): JsonNode =
%($x)
proc toJson*(x: object): JsonNode =
result = newJObject()
for name, value in x.fieldPairs:
if notNil(value):
result[name] = value.toJson()
proc toJson*(x: ref object): JsonNode =
result = newJObject()
if not x.isNil:
for name, value in x[].fieldPairs:
if notNil(value):
result[name] = value.toJson()
proc toJson*(x: JsonNode): JsonNode =
x
proc notNilAndValid(root: JsonNode, kind: JsonNodeKind): bool =
(not root.isNil) and (root.kind == kind)
proc fromJson*(root: JsonNode, x: var SomeInteger) =
if root.notNilAndValid(JInt):
x = type(x)(root.getInt())
if root.notNilAndValid(JFloat):
x = type(x)(root.getFloat())
proc fromJson*(root: JsonNode, x: var SomeFloat) =
if root.notNilAndValid(JFloat):
x = type(x)(root.getFloat())
if root.notNilAndValid(JInt):
x = type(x)(root.getInt())
proc fromJson*(root: JsonNode, x: var string) =
if root.notNilAndValid(JString):
x = root.getStr()
proc fromJson*(root: JsonNode, x: var bool) =
if root.notNilAndValid(JBool):
x = root.getBool()
proc fromJson*[T: enum](root: JsonNode, x: var T) =
if root.notNilAndValid(JString):
x = parseEnum[T](root.str)
proc fromJson*[T](root: JsonNode, x: var seq[T]) =
if root.notNilAndValid(JArray):
x.newSeq(root.len)
for i, value in x.mpairs:
root[i].fromJson(value)
proc fromJson*(root: JsonNode, x: var object) =
if root.notNilAndValid(JObject):
for name, value in x.fieldPairs:
root.getOrDefault(name).fromJson(value)
proc fromJson*(root: JsonNode, x: var ref object) =
if root.notNilAndValid(JObject):
x = type(x)()
for name, value in x[].fieldPairs:
root.getOrDefault(name).fromJson(value)
proc fromJson*(root: JsonNode, x: var JsonNode) =
x = root
template fromJson*[T](json: JsonNode, _: typedesc[T]): T =
var result: T
json.fromJson(result)
result
when isMainModule:
# test basics
echo "hello world".toJson.fromJson(string)
echo 1234.toJson.fromJson(int)
echo 123.456.toJson.fromJson(float)
echo true.toJson.fromJson(bool)
echo @[1,2,3].toJson.fromJson(seq[int])
when not defined(js):
# test supported integer sizes
echo (123.uint8).toJson.fromJson(uint8)
echo (-123.int8).toJson.fromJson(int8)
echo (1234.uint16).toJson.fromJson(uint16)
echo (-1234.int16).toJson.fromJson(int16)
echo (12356.uint32).toJson.fromJson(uint32)
echo (-12356.int32).toJson.fromJson(int32)
# test float sizes
echo (float32 123.678).toJson.fromJson(float32)
echo (float64 123.678).toJson.fromJson(float64)
echo parseJson("1").fromJson(float32)
echo parseJson("123").fromJson(float32)
echo parseJson("123.678901234567890").fromJson(float32)
echo parseJson("1").fromJson(float64)
echo parseJson("123").fromJson(float64)
echo parseJson("123.678901234567890").fromJson(float64)
# test enums
type Enumer = enum
Left
Right
Top
Bottom
let e = Top
echo e.toJson()
echo e.toJson().fromJson(Enumer)
echo parseJson(""" "Top" """).fromJson(Enumer)
echo parseJson(""" "top" """).fromJson(Enumer)
echo parseJson(""" "TOP" """).fromJson(Enumer)
# test regular objects
type Foo = object
id: int
name: string
time: float
active: bool
let foo = Foo(id: 32, name: "yes", time: 16.77, active: true)
echo foo.toJson()
echo parseJson("""
{"id":32,"name":"yes","time":16.77,"active":true}
""").fromJson(Foo)
echo parseJson("""{"id":32,"name":"yes","active":true}""").fromJson(Foo)
echo parseJson("""{}""").fromJson(Foo)
# int works in case of float, and float in case of int
echo parseJson("""{"id":32.0,"time":1677}""").fromJson(Foo)
echo @[1,2,3].toJson()
echo parseJson("""[1,2,3]""").fromJson(seq[int])
type Bar = object
id: int
arr: seq[int]
foo: Foo
var bar = Bar()
echo bar.toJson()
echo parseJson("""
{
"id": 123,
"arr": [
1,
2,
3
],
"foo": {
"id": 1,
"name": "hi",
"time": 12,
"active": true
}
}
""").fromJson(Bar)
echo parseJson("""
{
}
""").fromJson(Bar)
echo parseJson("""
{
"extra": 123
}
""").fromJson(Bar)
type
Foo2 = ref object
id: int
Bar2 = object
id: int
foo: Foo2
var foo2: Foo2
echo foo2.toJson()
var bar2 = Bar2()
echo bar2.toJson()
bar2.id = 2
bar2.foo = Foo2(id:4)
echo bar2.toJson()
echo parseJson("""
{
}
""").fromJson(Bar2)
echo parseJson("""
{
"id": 123
}
""").fromJson(Bar2)
bar2 = parseJson("""
{
"id": 123,
"foo": {"id": 456}
}
""").fromJson(Bar2)
echo bar2.foo.id
echo parseJson("""
{
"random": 123,
"json": {"id": 456}
}
""").toJson()
type
Foo3 = ref object
data: JsonNode
var foo3 = parseJson("""
{
"data": {"id": 456}
}
""").fromJson(Foo3)
echo foo3.data