discard """ file: "tjsonmacro.nim" output: "" """ import json, strutils, options, tables when isMainModule: # Tests inspired by own use case (with some additional tests). # This should succeed. type Point[T] = object x, y: T ReplayEventKind* = enum FoodAppeared, FoodEaten, DirectionChanged ReplayEvent* = object time*: float case kind*: ReplayEventKind of FoodAppeared, FoodEaten: foodPos*: Point[float] of DirectionChanged: playerPos*: float Replay* = ref object events*: seq[ReplayEvent] test: int test2: string test3: bool testNil: string var x = Replay( events: @[ ReplayEvent( time: 1.2345, kind: FoodEaten, foodPos: Point[float](x: 5.0, y: 1.0) ) ], test: 18827361, test2: "hello world", test3: true, testNil: nil ) let node = %x let y = to(node, Replay) doAssert y.events[0].time == 1.2345 doAssert y.events[0].kind == FoodEaten doAssert y.events[0].foodPos.x == 5.0 doAssert y.events[0].foodPos.y == 1.0 doAssert y.test == 18827361 doAssert y.test2 == "hello world" doAssert y.test3 doAssert y.testNil.isNil # Test for custom object variants (without an enum) and with an else branch. block: type TestVariant = object name: string case age: uint8 of 2: preSchool: string of 8: primarySchool: string else: other: int var node = %{ "name": %"Nim", "age": %8, "primarySchool": %"Sandtown" } var result = to(node, TestVariant) doAssert result.age == 8 doAssert result.name == "Nim" doAssert result.primarySchool == "Sandtown" node = %{ "name": %"⚔️Foo☢️", "age": %25, "other": %98 } result = to(node, TestVariant) doAssert result.name == node["name"].getStr() doAssert result.age == node["age"].getInt().uint8 doAssert result.other == node["other"].getBiggestInt() # TODO: Test object variant with set in of branch. # TODO: Should we support heterogenous arrays? # Tests that verify the error messages for invalid data. block: type Person = object name: string age: int var node = %{ "name": %"Dominik" } try: discard to(node, Person) doAssert false except KeyError as exc: doAssert("age" in exc.msg) except: doAssert false node["age"] = %false try: discard to(node, Person) doAssert false except JsonKindError as exc: doAssert("age" in exc.msg) except: doAssert false type PersonAge = enum Fifteen, Sixteen PersonCase = object name: string case age: PersonAge of Fifteen: discard of Sixteen: id: string try: discard to(node, PersonCase) doAssert false except JsonKindError as exc: doAssert("age" in exc.msg) except: doAssert false # Test the example in json module. block: let jsonNode = parseJson(""" { "person": { "name": "Nimmer", "age": 21 }, "list": [1, 2, 3, 4] } """) type Person = object name: string age: int Data1 = object # TODO: Codegen bug when changed to ``Data``. person: Person list: seq[int] var data = to(jsonNode, Data1) doAssert data.person.name == "Nimmer" doAssert data.person.age == 21 doAssert data.list == @[1, 2, 3, 4] # Test non-variant enum fields. block: type EnumType = enum Foo, Bar TestEnum = object field: EnumType var node = %{ "field": %"Bar" } var result = to(node, TestEnum) doAssert result.field == Bar # Test ref type in field. block: var jsonNode = parseJson(""" { "person": { "name": "Nimmer", "age": 21 }, "list": [1, 2, 3, 4] } """) type Person = ref object name: string age: int Data = object person: Person list: seq[int] var data = to(jsonNode, Data) doAssert data.person.name == "Nimmer" doAssert data.person.age == 21 doAssert data.list == @[1, 2, 3, 4] jsonNode = parseJson(""" { "person": null, "list": [1, 2, 3, 4] } """) data = to(jsonNode, Data) doAssert data.person.isNil block: type FooBar = object field: float let x = parseJson("""{ "field": 5}""") let data = to(x, FooBar) doAssert data.field == 5.0 block: type BirdColor = object name: string rgb: array[3, float] type Bird = object age: int height: float name: string colors: array[2, BirdColor] var red = BirdColor(name: "red", rgb: [1.0, 0.0, 0.0]) var blue = Birdcolor(name: "blue", rgb: [0.0, 0.0, 1.0]) var b = Bird(age: 3, height: 1.734, name: "bardo", colors: [red, blue]) let jnode = %b let data = jnode.to(Bird) doAssert data == b block: type MsgBase = ref object of RootObj name*: string MsgChallenge = ref object of MsgBase challenge*: string let data = %*{"name": "foo", "challenge": "bar"} let msg = data.to(MsgChallenge) doAssert msg.name == "foo" doAssert msg.challenge == "bar" block: type Color = enum Red, Brown Thing = object animal: tuple[fur: bool, legs: int] color: Color var j = parseJson(""" {"animal":{"fur":true,"legs":6},"color":"Red"} """) let parsed = to(j, Thing) doAssert parsed.animal.fur doAssert parsed.animal.legs == 6 doAssert parsed.color == Red block: type Car = object engine: tuple[name: string, capacity: float] model: string let j = """ {"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"} """ var i = 0 proc mulTest: JsonNode = i.inc() return parseJson(j) let parsed = mulTest().to(Car) doAssert parsed.engine.name == "V8" doAssert i == 1 block: # Option[T] support! type Car1 = object # TODO: Codegen bug when `Car` engine: tuple[name: string, capacity: Option[float]] model: string year: Option[int] let noYear = """ {"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"} """ let noYearParsed = parseJson(noYear) let noYearDeser = to(noYearParsed, Car1) doAssert noYearDeser.engine.capacity == some(5.5) doAssert noYearDeser.year.isNone doAssert noYearDeser.engine.name == "V8" # Table[T, Y] support. block: type Friend = object name: string age: int Dynamic = object name: string friends: Table[string, Friend] let data = """ {"friends": { "John": {"name": "John", "age": 35}, "Elizabeth": {"name": "Elizabeth", "age": 23} }, "name": "Dominik"} """ let dataParsed = parseJson(data) let dataDeser = to(dataParsed, Dynamic) doAssert dataDeser.name == "Dominik" doAssert dataDeser.friends["John"].age == 35 doAssert dataDeser.friends["Elizabeth"].age == 23 # JsonNode support block: type Test = object name: string fallback: JsonNode let data = """ {"name": "FooBar", "fallback": 56.42} """ let dataParsed = parseJson(data) let dataDeser = to(dataParsed, Test) doAssert dataDeser.name == "FooBar" doAssert dataDeser.fallback.kind == JFloat doAssert dataDeser.fallback.getFloat() == 56.42 # int64, float64 etc support. block: type Test1 = object a: int8 b: int16 c: int32 d: int64 e: uint8 f: uint16 g: uint32 h: uint64 i: float32 j: float64 let data = """ {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7, "h": 8, "i": 9.9, "j": 10.10} """ let dataParsed = parseJson(data) let dataDeser = to(dataParsed, Test1) doAssert dataDeser.a == 1 doAssert dataDeser.f == 6 doAssert dataDeser.i == 9.9'f32