* add distinct types to json 'to' macro * fix json 'to' macro and add more test
This commit is contained in:
parent
143834ba4e
commit
f92d61b1f4
2 changed files with 132 additions and 8 deletions
|
|
@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
|
||||||
exprColonExpr.add(ifStmt)
|
exprColonExpr.add(ifStmt)
|
||||||
|
|
||||||
proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
|
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] =
|
proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
|
||||||
## Process a field from a ``RecList``.
|
## Process a field from a ``RecList``.
|
||||||
##
|
##
|
||||||
|
|
@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
|
||||||
# Add the field value.
|
# Add the field value.
|
||||||
# -> jsonNode["`field`"]
|
# -> jsonNode["`field`"]
|
||||||
let indexedJsonNode = createJsonIndexer(jsonNode, $field)
|
let indexedJsonNode = createJsonIndexer(jsonNode, $field)
|
||||||
exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode))
|
let typeNode = detectDistinctType(field)
|
||||||
|
exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
|
||||||
of nnkRecCase:
|
of nnkRecCase:
|
||||||
# A "case" field that introduces a variant.
|
# A "case" field that introduces a variant.
|
||||||
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
let exprColonExpr = newNimNode(nnkExprColonExpr)
|
||||||
|
|
@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
let seqT = typeSym[1]
|
let seqT = typeSym[1]
|
||||||
let forLoopI = genSym(nskForVar, "i")
|
let forLoopI = genSym(nskForVar, "i")
|
||||||
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
|
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
|
||||||
let constructorNode = createConstructor(seqT, indexerNode)
|
let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
|
||||||
|
|
||||||
# Create a statement expression containing a for loop.
|
# Create a statement expression containing a for loop.
|
||||||
result = quote do:
|
result = quote do:
|
||||||
|
|
@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
|
|
||||||
# Handle all other types.
|
# Handle all other types.
|
||||||
let obj = getType(typeSym)
|
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`.
|
# When `Sym "Foo"` turns out to be a `ref object`.
|
||||||
result = createConstructor(obj, jsonNode)
|
result = createConstructor(obj, jsonNode)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
# TODO: The fact that `jsonNode` here works to give a good line number
|
# TODO: The fact that `jsonNode` here works to give a good line number
|
||||||
# is weird. Specifying typeSym should work but doesn't.
|
# is weird. Specifying typeSym should work but doesn't.
|
||||||
error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
|
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:
|
else:
|
||||||
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
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.person.age == 21
|
||||||
## doAssert data.list == @[1, 2, 3, 4]
|
## doAssert data.list == @[1, 2, 3, 4]
|
||||||
|
|
||||||
let typeNode = getTypeInst(T)
|
let typeNode = getTypeImpl(T)
|
||||||
expectKind(typeNode, nnkBracketExpr)
|
expectKind(typeNode, nnkBracketExpr)
|
||||||
doAssert(($typeNode[0]).normalize == "typedesc")
|
doAssert(($typeNode[0]).normalize == "typedesc")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -337,7 +337,7 @@ when isMainModule:
|
||||||
n2: Option[int]
|
n2: Option[int]
|
||||||
n3: Option[string]
|
n3: Option[string]
|
||||||
n4: Option[bool]
|
n4: Option[bool]
|
||||||
|
|
||||||
var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
|
var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
|
||||||
let j0Deser = j0.to(Obj)
|
let j0Deser = j0.to(Obj)
|
||||||
doAssert j0Deser.n1 == 1
|
doAssert j0Deser.n1 == 1
|
||||||
|
|
@ -411,10 +411,109 @@ when isMainModule:
|
||||||
doAssert dataDeser.a == 1
|
doAssert dataDeser.a == 1
|
||||||
doAssert dataDeser.f == 6
|
doAssert dataDeser.f == 6
|
||||||
doAssert dataDeser.i == 9.9'f32
|
doAssert dataDeser.i == 9.9'f32
|
||||||
|
|
||||||
# deserialize directly into a table
|
# deserialize directly into a table
|
||||||
block:
|
block:
|
||||||
let s = """{"a": 1, "b": 2}"""
|
let s = """{"a": 1, "b": 2}"""
|
||||||
let t = parseJson(s).to(Table[string, int])
|
let t = parseJson(s).to(Table[string, int])
|
||||||
doAssert t["a"] == 1
|
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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue