tjson.nim 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. discard """
  2. matrix: "--mm:refc"
  3. targets: "c cpp js"
  4. """
  5. #[
  6. Note: Macro tests are in tests/stdlib/tjsonmacro.nim
  7. ]#
  8. import std/[json,parsejson,strutils]
  9. from std/math import isNaN
  10. when not defined(js):
  11. import std/streams
  12. import stdtest/testutils
  13. from std/fenv import epsilon
  14. import std/[assertions, objectdollar]
  15. proc testRoundtrip[T](t: T, expected: string) =
  16. # checks that `T => json => T2 => json2` is such that json2 = json
  17. let j = %t
  18. doAssert $j == expected, $j
  19. doAssert %(j.to(T)) == j
  20. proc testRoundtripVal[T](t: T, expected: string) =
  21. # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T`
  22. # note that this isn't always possible, e.g. for pointer-like types or nans
  23. let j = %t
  24. doAssert $j == expected, $j
  25. let j2 = ($j).parseJson
  26. doAssert $j2 == expected, $(j2, t)
  27. let t2 = j2.to(T)
  28. doAssert t2 == t
  29. doAssert $(%* t2) == expected # sanity check, because -0.0 = 0.0 but their json representation differs
  30. let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}"""
  31. # nil passthrough
  32. doAssert(testJson{"doesnt_exist"}{"anything"}.isNil)
  33. testJson{["e", "f"]} = %true
  34. doAssert(testJson["e"]["f"].bval)
  35. # make sure UTF-16 decoding works.
  36. doAssert(testJson["c"].str == "🎃")
  37. doAssert(testJson["d"].str == "æ")
  38. # make sure no memory leek when parsing invalid string
  39. let startMemory = getOccupiedMem()
  40. for i in 0 .. 10000:
  41. try:
  42. discard parseJson"""{ invalid"""
  43. except:
  44. discard
  45. # memory diff should less than 4M
  46. doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # todo fixme doesn;t work for ORC
  47. # test `$`
  48. let stringified = $testJson
  49. let parsedAgain = parseJson(stringified)
  50. doAssert(parsedAgain["b"].str == "asd")
  51. parsedAgain["abc"] = %5
  52. doAssert parsedAgain["abc"].num == 5
  53. # Bounds checking
  54. when compileOption("boundChecks"):
  55. try:
  56. let a = testJson["a"][9]
  57. doAssert(false, "IndexDefect not thrown")
  58. except IndexDefect:
  59. discard
  60. try:
  61. let a = testJson["a"][-1]
  62. doAssert(false, "IndexDefect not thrown")
  63. except IndexDefect:
  64. discard
  65. try:
  66. doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value")
  67. except:
  68. doAssert(false, "IndexDefect thrown for valid index")
  69. doAssert(testJson{"b"}.getStr() == "asd", "Couldn't fetch a singly nested key with {}")
  70. doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
  71. doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
  72. doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
  73. doAssert(testJson{"a"} == parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
  74. doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
  75. # Generator:
  76. var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
  77. doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
  78. var j2 = %*
  79. [
  80. {
  81. "name": "John",
  82. "age": 30
  83. },
  84. {
  85. "name": "Susan",
  86. "age": 31
  87. }
  88. ]
  89. doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
  90. var name = "John"
  91. let herAge = 30
  92. const hisAge = 31
  93. var j3 = %*
  94. [ {"name": "John"
  95. , "age": herAge
  96. }
  97. , {"name": "Susan"
  98. , "age": hisAge
  99. }
  100. ]
  101. doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
  102. var j4 = %*{"test": nil}
  103. doAssert j4 == %{"test": newJNull()}
  104. let seqOfNodes = @[%1, %2]
  105. let jSeqOfNodes = %seqOfNodes
  106. doAssert(jSeqOfNodes[1].num == 2)
  107. type MyObj = object
  108. a, b: int
  109. s: string
  110. f32: float32
  111. f64: float64
  112. next: ref MyObj
  113. var m: MyObj
  114. m.s = "hi"
  115. m.a = 5
  116. let jMyObj = %m
  117. doAssert(jMyObj["a"].num == 5)
  118. doAssert(jMyObj["s"].str == "hi")
  119. # Test loading of file.
  120. when not defined(js):
  121. var parsed = parseFile("tests/testdata/jsontest.json")
  122. try:
  123. discard parsed["key2"][12123]
  124. doAssert(false)
  125. except IndexDefect: doAssert(true)
  126. var parsed2 = parseFile("tests/testdata/jsontest2.json")
  127. doAssert(parsed2{"repository", "description"}.str ==
  128. "IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
  129. doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ"
  130. doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887
  131. doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
  132. doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0014\"" # for #7887
  133. # Test with extra data
  134. when not defined(js):
  135. try:
  136. discard parseJson("123 456")
  137. doAssert(false)
  138. except JsonParsingError:
  139. doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
  140. try:
  141. discard parseFile("tests/testdata/jsonwithextradata.json")
  142. doAssert(false)
  143. except JsonParsingError:
  144. doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
  145. # bug #6438
  146. doAssert($ %*[] == "[]")
  147. doAssert($ %*{} == "{}")
  148. doAssert(not compiles(%{"error": "No messages"}))
  149. # bug #9111
  150. block:
  151. type
  152. Bar = string
  153. Foo = object
  154. a: int
  155. b: Bar
  156. let
  157. js = """{"a": 123, "b": "abc"}""".parseJson
  158. foo = js.to Foo
  159. doAssert(foo.b == "abc")
  160. # Generate constructors for range[T] types
  161. block:
  162. type
  163. Q1 = range[0'u8 .. 50'u8]
  164. Q2 = range[0'u16 .. 50'u16]
  165. Q3 = range[0'u32 .. 50'u32]
  166. Q4 = range[0'i8 .. 50'i8]
  167. Q5 = range[0'i16 .. 50'i16]
  168. Q6 = range[0'i32 .. 50'i32]
  169. Q7 = range[0'f32 .. 50'f32]
  170. Q8 = range[0'f64 .. 50'f64]
  171. Q9 = range[0 .. 50]
  172. X = object
  173. m1: Q1
  174. m2: Q2
  175. m3: Q3
  176. m4: Q4
  177. m5: Q5
  178. m6: Q6
  179. m7: Q7
  180. m8: Q8
  181. m9: Q9
  182. let obj = X(
  183. m1: Q1(42),
  184. m2: Q2(42),
  185. m3: Q3(42),
  186. m4: Q4(42),
  187. m5: Q5(42),
  188. m6: Q6(42),
  189. m7: Q7(42),
  190. m8: Q8(42),
  191. m9: Q9(42)
  192. )
  193. doAssert(obj == to(%obj, type(obj)))
  194. when not defined(js):
  195. const fragments = """[1,2,3] {"hi":3} 12 [] """
  196. var res = ""
  197. for x in parseJsonFragments(newStringStream(fragments)):
  198. res.add($x)
  199. res.add " "
  200. doAssert res == fragments
  201. # test isRefSkipDistinct
  202. type
  203. MyRef = ref object
  204. MyObject = object
  205. MyDistinct = distinct MyRef
  206. MyOtherDistinct = distinct MyRef
  207. var x0: ref int
  208. var x1: MyRef
  209. var x2: MyObject
  210. var x3: MyDistinct
  211. var x4: MyOtherDistinct
  212. doAssert isRefSkipDistinct(x0)
  213. doAssert isRefSkipDistinct(x1)
  214. doAssert not isRefSkipDistinct(x2)
  215. doAssert isRefSkipDistinct(x3)
  216. doAssert isRefSkipDistinct(x4)
  217. doAssert isRefSkipDistinct(ref int)
  218. doAssert isRefSkipDistinct(MyRef)
  219. doAssert not isRefSkipDistinct(MyObject)
  220. doAssert isRefSkipDistinct(MyDistinct)
  221. doAssert isRefSkipDistinct(MyOtherDistinct)
  222. let x = parseJson("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999")
  223. doAssert x.kind == JString
  224. block: # bug #15835
  225. type
  226. Foo = object
  227. ii*: int
  228. data*: JsonNode
  229. block:
  230. const jt = """{"ii": 123, "data": ["some", "data"]}"""
  231. let js = parseJson(jt)
  232. discard js.to(Foo)
  233. block:
  234. const jt = """{"ii": 123}"""
  235. let js = parseJson(jt)
  236. doAssertRaises(KeyError):
  237. echo js.to(Foo)
  238. type
  239. ContentNodeKind* = enum
  240. P,
  241. Br,
  242. Text,
  243. ContentNode* = object
  244. case kind*: ContentNodeKind
  245. of P: pChildren*: seq[ContentNode]
  246. of Br: nil
  247. of Text: textStr*: string
  248. let mynode = ContentNode(kind: P, pChildren: @[
  249. ContentNode(kind: Text, textStr: "mychild"),
  250. ContentNode(kind: Br)
  251. ])
  252. doAssert $mynode == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])"""
  253. let jsonNode = %*mynode
  254. doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}"""
  255. doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])"""
  256. block: # bug #17383
  257. testRoundtrip(int32.high): "2147483647"
  258. testRoundtrip(uint32.high): "4294967295"
  259. when int.sizeof == 4:
  260. testRoundtrip(int.high): "2147483647"
  261. testRoundtrip(uint.high): "4294967295"
  262. else:
  263. testRoundtrip(int.high): "9223372036854775807"
  264. testRoundtrip(uint.high): "18446744073709551615"
  265. when not defined(js):
  266. testRoundtrip(int64.high): "9223372036854775807"
  267. testRoundtrip(uint64.high): "18446744073709551615"
  268. block: # bug #18007
  269. testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]"""
  270. # pending https://github.com/nim-lang/Nim/issues/18025 use:
  271. # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0])
  272. let inf = float32(Inf)
  273. testRoundtrip([float32(NaN), inf, -inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]"""
  274. when not defined(js): # because of Infinity vs inf
  275. testRoundtripVal([inf, -inf, 0.0, -0.0, 1.0]): """["inf","-inf",0.0,-0.0,1.0]"""
  276. let a = parseJson($(%NaN)).to(float)
  277. doAssert a.isNaN
  278. whenRuntimeJs: discard # refs bug #18009
  279. do:
  280. testRoundtripVal(0.0): "0.0"
  281. testRoundtripVal(-0.0): "-0.0"
  282. block: # bug #15397, bug #13196
  283. testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002"
  284. testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568"
  285. block:
  286. let a = "18446744073709551615"
  287. let b = a.parseJson
  288. doAssert b.kind == JString
  289. let c = $b
  290. when defined(js):
  291. doAssert c == "18446744073709552000"
  292. else:
  293. doAssert c == "18446744073709551615"
  294. block:
  295. let a = """
  296. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  297. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  298. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  299. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  300. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  301. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  302. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  303. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  304. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  305. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  306. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  307. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  308. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  309. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  310. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  311. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  312. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  313. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  314. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  315. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  316. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  317. [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
  318. """
  319. when not defined(js):
  320. try:
  321. discard parseJson(a)
  322. except JsonParsingError:
  323. doAssert getCurrentExceptionMsg().contains("] expected")