From 4fa73b442b87c90854d377c7854e791e8a254b38 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 5 Jun 2020 02:06:02 -0700 Subject: [PATCH] make toJson more feature complete --- lib/pure/json.nim | 43 +++++++++++++++++++++++++++---------- tests/stdlib/tjsonmacro.nim | 13 ++++++++++- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index c682a730896d5..80eb1be4b6845 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -370,20 +370,20 @@ 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) @@ -391,7 +391,7 @@ proc toJson(x: NimNode): NimNode {.compileTime.} = 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) @@ -399,7 +399,7 @@ proc toJson(x: NimNode): NimNode {.compileTime.} = 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 @@ -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 diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 6bb4be23e41aa..a5a1b53a23208 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -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()