mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 13:30:33 +00:00
* add distinct types to json 'to' macro * fix json 'to' macro and add more test
This commit is contained in:
committed by
Dominik Picheta
parent
143834ba4e
commit
f92d61b1f4
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user