Skip to content

Commit

Permalink
make toJson more feature complete
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Jun 5, 2020
1 parent a568c10 commit 4fa73b4
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 12 deletions.
43 changes: 32 additions & 11 deletions lib/pure/json.nim
Original file line number Diff line number Diff line change
Expand Up @@ -370,36 +370,36 @@ proc `%`*(o: enum): JsonNode =
## string. Creates a new ``JString JsonNode``.
result = %($o)

proc toJson(x: NimNode): NimNode {.compileTime.} =
proc toJsonImpl(x: NimNode): NimNode {.compileTime.} =
case x.kind
of nnkBracket: # array
if x.len == 0: return newCall(bindSym"newJArray")
result = newNimNode(nnkBracket)
for i in 0 ..< x.len:
result.add(toJson(x[i]))
result.add(toJsonImpl(x[i]))
result = newCall(bindSym("%", brOpen), result)
of nnkTableConstr: # object
if x.len == 0: return newCall(bindSym"newJObject")
result = newNimNode(nnkTableConstr)
for i in 0 ..< x.len:
x[i].expectKind nnkExprColonExpr
result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
result.add newTree(nnkExprColonExpr, x[i][0], toJsonImpl(x[i][1]))
result = newCall(bindSym("%", brOpen), result)
of nnkCurly: # empty object
x.expectLen(0)
result = newCall(bindSym"newJObject")
of nnkNilLit:
result = newCall(bindSym"newJNull")
of nnkPar:
if x.len == 1: result = toJson(x[0])
if x.len == 1: result = toJsonImpl(x[0])
else: result = newCall(bindSym("%", brOpen), x)
else:
result = newCall(bindSym("%", brOpen), x)

macro `%*`*(x: untyped): untyped =
## Convert an expression to a JsonNode directly, without having to specify
## `%` for every element.
result = toJson(x)
result = toJsonImpl(x)

proc `==`*(a, b: JsonNode): bool =
## Check two nodes for equality
Expand Down Expand Up @@ -1264,17 +1264,38 @@ when false:
# { "json": 5 }
# To get that we shall use, obj["json"]

proc toJson2*[T](a: T): JsonNode =
## allows custom serialization
proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}

proc toJson*[T](a: T): JsonNode =
## like `%` but allows custom serialization hook if `serialize(a: T)` is in scope
when compiles(serialize(a)):
proc funAdd(t: JsonNode, key: string, val: string) = t[key] = %val
proc funAdd2(t: JsonNode, key: string, val: JsonNode) = t[key] = val
proc funObj(): JsonNode = newJObject()
result = serialize(a, funObj, funAdd, funAdd2)
elif T is object:
result = newJObject()
for k,v in fieldPairs(a):
result[k] = toJson2(v)
elif T is object | tuple:
const isNamed = T is object or isNamedTuple(T)
when isNamed:
result = newJObject()
for k, v in a.fieldPairs: result[k] = toJson(v)
else:
result = newJArray()
for v in a.fields: result.add toJson(v)
elif T is ref | ptr:
if a == nil: result = newJNull()
else: result = toJson(a[])
elif T is array | seq:
result = newJArray()
for ai in a:
result.add toJson(ai)
elif T is pointer:
result = toJson(cast[int](a))
elif T is distinct:
result = toJson(a.distinctBase)
elif T is bool:
result = %(a)
elif T is Ordinal:
result = %(cast[int](a))
else:
result = %a

Expand Down
13 changes: 12 additions & 1 deletion tests/stdlib/tjsonmacro.nim
Original file line number Diff line number Diff line change
Expand Up @@ -638,8 +638,19 @@ static:
testJson()

import strtabs

proc testCustom()=
var t = {"name": "John", "city": "Monaco"}.newStringTable
let s = toJson2(t)
let s = toJson(t)
doAssert $s == """{"mode":"modeCaseSensitive","table":{"city":"Monaco","name":"John"}}"""

proc testToJson() =
var t = {"z": "Z", "y": "Y"}.newStringTable
type A = ref object
a1: string
let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default))
let j = a.toJson
doAssert $j == """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]"""

testCustom()
testToJson()

0 comments on commit 4fa73b4

Please sign in to comment.