diff options
-rw-r--r-- | lib/pure/json.nim | 35 | ||||
-rw-r--r-- | tests/stdlib/tjsonmacro.nim | 105 |
2 files changed, 132 insertions, 8 deletions
diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 67f92dffe..b9279b18c 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType, exprColonExpr.add(ifStmt) proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.} + +proc detectDistinctType(typeSym: NimNode): NimNode = + let + typeImpl = getTypeImpl(typeSym) + typeInst = getTypeInst(typeSym) + result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst + proc processObjField(field, jsonNode: NimNode): seq[NimNode] = ## Process a field from a ``RecList``. ## @@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] = # Add the field value. # -> jsonNode["`field`"] let indexedJsonNode = createJsonIndexer(jsonNode, $field) - exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode)) - + let typeNode = detectDistinctType(field) + exprColonExpr.add(createConstructor(typeNode, indexedJsonNode)) of nnkRecCase: # A "case" field that introduces a variant. let exprColonExpr = newNimNode(nnkExprColonExpr) @@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = let seqT = typeSym[1] let forLoopI = genSym(nskForVar, "i") let indexerNode = createJsonIndexer(jsonNode, forLoopI) - let constructorNode = createConstructor(seqT, indexerNode) + let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode) # Create a statement expression containing a for loop. result = quote do: @@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = # Handle all other types. let obj = getType(typeSym) - if obj.kind == nnkBracketExpr: + let typeNode = getTypeImpl(typeSym) + if typeNode.typeKind == ntyDistinct: + result = createConstructor(typeNode, jsonNode) + elif obj.kind == nnkBracketExpr: # When `Sym "Foo"` turns out to be a `ref object`. result = createConstructor(obj, jsonNode) else: @@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = # TODO: The fact that `jsonNode` here works to give a good line number # is weird. Specifying typeSym should work but doesn't. error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode) + of nnkDistinctTy: + var baseType = typeSym + # solve nested distinct types + while baseType.typeKind == ntyDistinct: + let impl = getTypeImpl(baseType[0]) + if impl.typeKind != ntyDistinct: + baseType = baseType[0] + break + baseType = impl + let ret = createConstructor(baseType, jsonNode) + let typeInst = getTypeInst(typeSym) + result = quote do: + ( + `typeInst`(`ret`) + ) else: doAssert false, "Unable to create constructor for: " & $typeSym.kind @@ -1418,7 +1443,7 @@ macro to*(node: JsonNode, T: typedesc): untyped = ## doAssert data.person.age == 21 ## doAssert data.list == @[1, 2, 3, 4] - let typeNode = getTypeInst(T) + let typeNode = getTypeImpl(T) expectKind(typeNode, nnkBracketExpr) doAssert(($typeNode[0]).normalize == "typedesc") diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index f13d2e5cb..c0b4d5f78 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -337,7 +337,7 @@ when isMainModule: n2: Option[int] n3: Option[string] n4: Option[bool] - + var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""") let j0Deser = j0.to(Obj) doAssert j0Deser.n1 == 1 @@ -411,10 +411,109 @@ when isMainModule: doAssert dataDeser.a == 1 doAssert dataDeser.f == 6 doAssert dataDeser.i == 9.9'f32 - + # deserialize directly into a table block: let s = """{"a": 1, "b": 2}""" let t = parseJson(s).to(Table[string, int]) doAssert t["a"] == 1 - doAssert t["b"] == 2 \ No newline at end of file + doAssert t["b"] == 2 + + block: + # bug #8037 + type + Apple = distinct string + String = distinct Apple + Email = distinct string + MyList = distinct seq[int] + MyYear = distinct Option[int] + MyTable = distinct Table[string, int] + MyArr = distinct array[3, float] + MyRef = ref object + name: string + MyObj = object + color: int + MyDistRef = distinct MyRef + MyDistObj = distinct MyObj + Toot = object + name*: String + email*: Email + list: MyList + year: MyYear + dict: MyTable + arr: MyArr + person: MyDistRef + distfruit: MyDistObj + dog: MyRef + fruit: MyObj + emails: seq[String] + + var tJson = parseJson(""" + { + "name":"Bongo", + "email":"bongo@bingo.com", + "list": [11,7,15], + "year": 1975, + "dict": {"a": 1, "b": 2}, + "arr": [1.0, 2.0, 7.0], + "person": {"name": "boney"}, + "dog": {"name": "honey"}, + "fruit": {"color": 10}, + "distfruit": {"color": 11}, + "emails": ["abc", "123"] + } + """) + + var t = to(tJson, Toot) + doAssert string(t.name) == "Bongo" + doAssert string(t.email) == "bongo@bingo.com" + doAssert seq[int](t.list) == @[11,7,15] + doAssert Option[int](t.year).get() == 1975 + doAssert Table[string,int](t.dict)["a"] == 1 + doAssert Table[string,int](t.dict)["b"] == 2 + doAssert array[3, float](t.arr) == [1.0,2.0,7.0] + doAssert MyRef(t.person).name == "boney" + doAssert MyObj(t.distFruit).color == 11 + doAssert t.dog.name == "honey" + doAssert t.fruit.color == 10 + doAssert seq[string](t.emails) == @["abc", "123"] + + block test_table: + var y = parseJson("""{"a": 1, "b": 2, "c": 3}""") + var u = y.to(MyTable) + var v = y.to(Table[string, int]) + doAssert Table[string, int](u)["a"] == 1 + doAssert Table[string, int](u)["b"] == 2 + doAssert Table[string, int](u)["c"] == 3 + doAssert v["a"] == 1 + + block primitive_string: + const kApple = "apple" + var u = newJString(kApple) + var v = u.to(Email) + var w = u.to(Apple) + var x = u.to(String) + doAssert string(v) == kApple + doAssert string(w) == kApple + doAssert string(x) == kApple + + block test_option: + var u = newJInt(1137) + var v = u.to(MyYear) + var w = u.to(Option[int]) + doAssert Option[int](v).get() == 1137 + doAssert w.get() == 1137 + + block test_object: + var u = parseJson("""{"color": 987}""") + var v = u.to(MyObj) + var w = u.to(MyDistObj) + doAssert v.color == 987 + doAssert MyObj(w).color == 987 + + block test_ref_object: + var u = parseJson("""{"name": "smith"}""") + var v = u.to(MyRef) + var w = u.to(MyDistRef) + doAssert v.name == "smith" + doAssert MyRef(w).name == "smith" |