123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641 |
- discard """
- output: ""
- targets: "c js"
- """
- import json, strutils, options, tables
- # The definition of the `%` proc needs to be here, since the `% c` calls below
- # can only find our custom `%` proc for `Pix` if defined in global scope.
- type
- Pix = tuple[x, y: uint8, ch: uint16]
- proc `%`(p: Pix): JsonNode =
- result = %* { "x" : % p.x,
- "y" : % p.y,
- "ch" : % p.ch }
- proc testJson() =
- # 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]
- case subKind*: bool
- of true:
- it: int
- of false:
- ot: 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),
- subKind: true,
- it: 7
- )
- ],
- 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 == "nil"
- # 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 heterogeneous 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:
- when not defined(js):
- # disable on js because of #12492
- 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 =
- inc i
- 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"
- # Issue #7433
- type
- Obj2 = object
- n1: int
- n2: Option[string]
- n3: bool
- var j = %*[ { "n1": 4, "n2": "ABC", "n3": true },
- { "n1": 1, "n3": false },
- { "n1": 1, "n2": "XYZ", "n3": false } ]
- let jDeser = j.to(seq[Obj2])
- doAssert jDeser[0].n2.get() == "ABC"
- doAssert jDeser[1].n2.isNone()
- # Issue #6902
- type
- Obj = object
- n1: int
- 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
- doAssert j0Deser.n2.isNone()
- doAssert j0Deser.n3.isNone()
- doAssert j0Deser.n4.isNone()
- # 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
- # deserialize directly into a table
- block:
- let s = """{"a": 1, "b": 2}"""
- let t = parseJson(s).to(Table[string, int])
- when not defined(js):
- # For some reason on the JS backend `{"b": 2, "a": 0}` is
- # sometimes the value of `t`. This needs investigation. I can't
- # reproduce it right now in an isolated test.
- doAssert t["a"] == 1
- 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"
- block:
- # bug #12015
- type
- Cluster = object
- works: tuple[x, y: uint8, ch: uint16] # working
- fails: Pix # previously broken
- let data = (x: 123'u8, y: 53'u8, ch: 1231'u16)
- let c = Cluster(works: data, fails: data)
- let cFromJson = (% c).to(Cluster)
- doAssert c == cFromJson
- block:
- # bug related to #12015
- type
- PixInt = tuple[x, y, ch: int]
- SomePix = Pix | PixInt
- Cluster[T: SomePix] = seq[T]
- ClusterObject[T: SomePix] = object
- data: Cluster[T]
- RecoEvent[T: SomePix] = object
- cluster: seq[ClusterObject[T]]
- let data = @[(x: 123'u8, y: 53'u8, ch: 1231'u16)]
- var c = RecoEvent[Pix](cluster: @[ClusterObject[Pix](data: data)])
- let cFromJson = (% c).to(RecoEvent[Pix])
- doAssert c == cFromJson
- block:
- # ref objects with cycles.
- type
- Misdirection = object
- cycle: Cycle
- Cycle = ref object
- foo: string
- cycle: Misdirection
- let data = """
- {"cycle": null}
- """
- let dataParsed = parseJson(data)
- let dataDeser = to(dataParsed, Misdirection)
- block:
- # ref object from #12316
- type
- Foo = ref Bar
- Bar = object
- discard "null".parseJson.to Foo
- block:
- # named array #12289
- type Vec = array[2, int]
- let arr = "[1,2]".parseJson.to Vec
- doAssert arr == [1,2]
- block:
- # test error message in exception
- type
- MyType = object
- otherMember: string
- member: MySubType
- MySubType = object
- somethingElse: string
- list: seq[MyData]
- MyData = object
- value: int
- let jsonNode = parseJson("""
- {
- "otherMember": "otherValue",
- "member": {
- "somethingElse": "something",
- "list": [{"value": 1}, {"value": 2}, {}]
- }
- }
- """)
- try:
- let tmp = jsonNode.to(MyType)
- doAssert false, "this should be unreachable"
- except KeyError:
- doAssert getCurrentExceptionMsg().contains ".member.list[2].value"
- testJson()
- static:
- testJson()
|