json.nim 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf, Dominik Picheta
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements a simple high performance `JSON`:idx:
  10. ## parser. JSON (JavaScript Object Notation) is a lightweight
  11. ## data-interchange format that is easy for humans to read and write
  12. ## (unlike XML). It is easy for machines to parse and generate.
  13. ## JSON is based on a subset of the JavaScript Programming Language,
  14. ## Standard ECMA-262 3rd Edition - December 1999.
  15. ##
  16. ## Overview
  17. ## ========
  18. ##
  19. ## Parsing JSON
  20. ## ------------
  21. ##
  22. ## JSON often arrives into your program (via an API or a file) as a ``string``.
  23. ## The first step is to change it from its serialized form into a nested object
  24. ## structure called a ``JsonNode``.
  25. ##
  26. ## The ``parseJson`` procedure takes a string containing JSON and returns a
  27. ## ``JsonNode`` object. This is an object variant and it is either a
  28. ## ``JObject``, ``JArray``, ``JString``, ``JInt``, ``JFloat``, ``JBool`` or
  29. ## ``JNull``. You check the kind of this object variant by using the ``kind``
  30. ## accessor.
  31. ##
  32. ## For a ``JsonNode`` who's kind is ``JObject``, you can acess its fields using
  33. ## the ``[]`` operator. The following example shows how to do this:
  34. ##
  35. ## .. code-block:: Nim
  36. ## import json
  37. ##
  38. ## let jsonNode = parseJson("""{"key": 3.14}""")
  39. ##
  40. ## doAssert jsonNode.kind == JObject
  41. ## doAssert jsonNode["key"].kind == JFloat
  42. ##
  43. ## Reading values
  44. ## --------------
  45. ##
  46. ## Once you have a ``JsonNode``, retrieving the values can then be achieved
  47. ## by using one of the helper procedures, which include:
  48. ##
  49. ## * ``getInt``
  50. ## * ``getFloat``
  51. ## * ``getStr``
  52. ## * ``getBool``
  53. ##
  54. ## To retrieve the value of ``"key"`` you can do the following:
  55. ##
  56. ## .. code-block:: Nim
  57. ## import json
  58. ##
  59. ## let jsonNode = parseJson("""{"key": 3.14}""")
  60. ##
  61. ## doAssert jsonNode["key"].getFloat() == 3.14
  62. ##
  63. ## **Important:** The ``[]`` operator will raise an exception when the
  64. ## specified field does not exist.
  65. ##
  66. ## Handling optional keys
  67. ## ----------------------
  68. ##
  69. ## By using the ``{}`` operator instead of ``[]``, it will return ``nil``
  70. ## when the field is not found. The ``get``-family of procedures will return a
  71. ## type's default value when called on ``nil``.
  72. ##
  73. ## .. code-block:: Nim
  74. ## import json
  75. ##
  76. ## let jsonNode = parseJson("{}")
  77. ##
  78. ## doAssert jsonNode{"nope"}.getInt() == 0
  79. ## doAssert jsonNode{"nope"}.getFloat() == 0
  80. ## doAssert jsonNode{"nope"}.getStr() == ""
  81. ## doAssert jsonNode{"nope"}.getBool() == false
  82. ##
  83. ## Using default values
  84. ## --------------------
  85. ##
  86. ## The ``get``-family helpers also accept an additional parameter which allow
  87. ## you to fallback to a default value should the key's values be ``null``:
  88. ##
  89. ## .. code-block:: Nim
  90. ## import json
  91. ##
  92. ## let jsonNode = parseJson("""{"key": 3.14, "key2": null}""")
  93. ##
  94. ## doAssert jsonNode["key"].getFloat(6.28) == 3.14
  95. ## doAssert jsonNode["key2"].getFloat(3.14) == 3.14
  96. ## doAssert jsonNode{"nope"}.getFloat(3.14) == 3.14 # note the {}
  97. ##
  98. ## Unmarshalling
  99. ## -------------
  100. ##
  101. ## In addition to reading dynamic data, Nim can also unmarshall JSON directly
  102. ## into a type with the ``to`` macro.
  103. ##
  104. ## .. code-block:: Nim
  105. ## import json
  106. ##
  107. ## type
  108. ## User = object
  109. ## name: string
  110. ## age: int
  111. ##
  112. ## let userJson = parseJson("""{ "name": "Nim", "age": 12 }""")
  113. ## let user = to(userJson, User)
  114. ##
  115. ## Creating JSON
  116. ## =============
  117. ##
  118. ## This module can also be used to comfortably create JSON using the ``%*``
  119. ## operator:
  120. ##
  121. ## .. code-block:: nim
  122. ## import json
  123. ##
  124. ## var hisName = "John"
  125. ## let herAge = 31
  126. ## var j = %*
  127. ## [
  128. ## { "name": hisName, "age": 30 },
  129. ## { "name": "Susan", "age": herAge }
  130. ## ]
  131. ##
  132. ## var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
  133. ## j2["details"] = %* {"age":35, "pi":3.1415}
  134. ## echo j2
  135. runnableExamples:
  136. ## Note: for JObject, key ordering is preserved, unlike in some languages,
  137. ## this is convenient for some use cases. Example:
  138. type Foo = object
  139. a1, a2, a0, a3, a4: int
  140. doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""
  141. import
  142. hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson
  143. export
  144. tables.`$`
  145. export
  146. parsejson.JsonEventKind, parsejson.JsonError, JsonParser, JsonKindError,
  147. open, close, str, getInt, getFloat, kind, getColumn, getLine, getFilename,
  148. errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr
  149. when defined(nimJsonGet):
  150. {.pragma: deprecatedGet, deprecated.}
  151. else:
  152. {.pragma: deprecatedGet.}
  153. type
  154. JsonNodeKind* = enum ## possible JSON node types
  155. JNull,
  156. JBool,
  157. JInt,
  158. JFloat,
  159. JString,
  160. JObject,
  161. JArray
  162. JsonNode* = ref JsonNodeObj ## JSON node
  163. JsonNodeObj* {.acyclic.} = object
  164. case kind*: JsonNodeKind
  165. of JString:
  166. str*: string
  167. of JInt:
  168. num*: BiggestInt
  169. of JFloat:
  170. fnum*: float
  171. of JBool:
  172. bval*: bool
  173. of JNull:
  174. nil
  175. of JObject:
  176. fields*: OrderedTable[string, JsonNode]
  177. of JArray:
  178. elems*: seq[JsonNode]
  179. proc newJString*(s: string): JsonNode =
  180. ## Creates a new `JString JsonNode`.
  181. new(result)
  182. result.kind = JString
  183. result.str = s
  184. proc newJStringMove(s: string): JsonNode =
  185. new(result)
  186. result.kind = JString
  187. shallowCopy(result.str, s)
  188. proc newJInt*(n: BiggestInt): JsonNode =
  189. ## Creates a new `JInt JsonNode`.
  190. new(result)
  191. result.kind = JInt
  192. result.num = n
  193. proc newJFloat*(n: float): JsonNode =
  194. ## Creates a new `JFloat JsonNode`.
  195. new(result)
  196. result.kind = JFloat
  197. result.fnum = n
  198. proc newJBool*(b: bool): JsonNode =
  199. ## Creates a new `JBool JsonNode`.
  200. new(result)
  201. result.kind = JBool
  202. result.bval = b
  203. proc newJNull*(): JsonNode =
  204. ## Creates a new `JNull JsonNode`.
  205. new(result)
  206. proc newJObject*(): JsonNode =
  207. ## Creates a new `JObject JsonNode`
  208. new(result)
  209. result.kind = JObject
  210. result.fields = initOrderedTable[string, JsonNode](4)
  211. proc newJArray*(): JsonNode =
  212. ## Creates a new `JArray JsonNode`
  213. new(result)
  214. result.kind = JArray
  215. result.elems = @[]
  216. proc getStr*(n: JsonNode, default: string = ""): string =
  217. ## Retrieves the string value of a `JString JsonNode`.
  218. ##
  219. ## Returns ``default`` if ``n`` is not a ``JString``, or if ``n`` is nil.
  220. if n.isNil or n.kind != JString: return default
  221. else: return n.str
  222. proc getInt*(n: JsonNode, default: int = 0): int =
  223. ## Retrieves the int value of a `JInt JsonNode`.
  224. ##
  225. ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
  226. if n.isNil or n.kind != JInt: return default
  227. else: return int(n.num)
  228. proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
  229. ## Retrieves the BiggestInt value of a `JInt JsonNode`.
  230. ##
  231. ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
  232. if n.isNil or n.kind != JInt: return default
  233. else: return n.num
  234. proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated: "use getInt or getBiggestInt instead".} =
  235. ## **Deprecated since v0.18.2:** use ``getInt`` or ``getBiggestInt`` instead.
  236. getBiggestInt(n, default)
  237. proc getFloat*(n: JsonNode, default: float = 0.0): float =
  238. ## Retrieves the float value of a `JFloat JsonNode`.
  239. ##
  240. ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil.
  241. if n.isNil: return default
  242. case n.kind
  243. of JFloat: return n.fnum
  244. of JInt: return float(n.num)
  245. else: return default
  246. proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated: "use getFloat instead".} =
  247. ## **Deprecated since v0.18.2:** use ``getFloat`` instead.
  248. getFloat(n, default)
  249. proc getBool*(n: JsonNode, default: bool = false): bool =
  250. ## Retrieves the bool value of a `JBool JsonNode`.
  251. ##
  252. ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil.
  253. if n.isNil or n.kind != JBool: return default
  254. else: return n.bval
  255. proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated: "use getBool instead".} =
  256. ## **Deprecated since v0.18.2:** use ``getBool`` instead.
  257. getBool(n, default)
  258. proc getFields*(n: JsonNode,
  259. default = initOrderedTable[string, JsonNode](4)):
  260. OrderedTable[string, JsonNode] =
  261. ## Retrieves the key, value pairs of a `JObject JsonNode`.
  262. ##
  263. ## Returns ``default`` if ``n`` is not a ``JObject``, or if ``n`` is nil.
  264. if n.isNil or n.kind != JObject: return default
  265. else: return n.fields
  266. proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] =
  267. ## Retrieves the array of a `JArray JsonNode`.
  268. ##
  269. ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil.
  270. if n.isNil or n.kind != JArray: return default
  271. else: return n.elems
  272. proc add*(father, child: JsonNode) =
  273. ## Adds `child` to a JArray node `father`.
  274. assert father.kind == JArray
  275. father.elems.add(child)
  276. proc add*(obj: JsonNode, key: string, val: JsonNode) =
  277. ## Sets a field from a `JObject`.
  278. assert obj.kind == JObject
  279. obj.fields[key] = val
  280. proc `%`*(s: string): JsonNode =
  281. ## Generic constructor for JSON data. Creates a new `JString JsonNode`.
  282. new(result)
  283. result.kind = JString
  284. result.str = s
  285. proc `%`*(n: BiggestInt): JsonNode =
  286. ## Generic constructor for JSON data. Creates a new `JInt JsonNode`.
  287. new(result)
  288. result.kind = JInt
  289. result.num = n
  290. proc `%`*(n: float): JsonNode =
  291. ## Generic constructor for JSON data. Creates a new `JFloat JsonNode`.
  292. new(result)
  293. result.kind = JFloat
  294. result.fnum = n
  295. proc `%`*(b: bool): JsonNode =
  296. ## Generic constructor for JSON data. Creates a new `JBool JsonNode`.
  297. new(result)
  298. result.kind = JBool
  299. result.bval = b
  300. proc `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
  301. ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
  302. if keyvals.len == 0: return newJArray()
  303. result = newJObject()
  304. for key, val in items(keyVals): result.fields[key] = val
  305. template `%`*(j: JsonNode): JsonNode = j
  306. proc `%`*[T](elements: openArray[T]): JsonNode =
  307. ## Generic constructor for JSON data. Creates a new `JArray JsonNode`
  308. result = newJArray()
  309. for elem in elements: result.add(%elem)
  310. when false:
  311. # For 'consistency' we could do this, but that only pushes people further
  312. # into that evil comfort zone where they can use Nim without understanding it
  313. # causing problems later on.
  314. proc `%`*(elements: set[bool]): JsonNode =
  315. ## Generic constructor for JSON data. Creates a new `JObject JsonNode`.
  316. ## This can only be used with the empty set ``{}`` and is supported
  317. ## to prevent the gotcha ``%*{}`` which used to produce an empty
  318. ## JSON array.
  319. result = newJObject()
  320. assert false notin elements, "usage error: only empty sets allowed"
  321. assert true notin elements, "usage error: only empty sets allowed"
  322. proc `%`*(o: object): JsonNode =
  323. ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
  324. result = newJObject()
  325. for k, v in o.fieldPairs: result[k] = %v
  326. proc `%`*(o: ref object): JsonNode =
  327. ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
  328. if o.isNil:
  329. result = newJNull()
  330. else:
  331. result = %(o[])
  332. proc `%`*(o: enum): JsonNode =
  333. ## Construct a JsonNode that represents the specified enum value as a
  334. ## string. Creates a new ``JString JsonNode``.
  335. result = %($o)
  336. proc toJson(x: NimNode): NimNode {.compiletime.} =
  337. case x.kind
  338. of nnkBracket: # array
  339. if x.len == 0: return newCall(bindSym"newJArray")
  340. result = newNimNode(nnkBracket)
  341. for i in 0 ..< x.len:
  342. result.add(toJson(x[i]))
  343. result = newCall(bindSym("%", brOpen), result)
  344. of nnkTableConstr: # object
  345. if x.len == 0: return newCall(bindSym"newJObject")
  346. result = newNimNode(nnkTableConstr)
  347. for i in 0 ..< x.len:
  348. x[i].expectKind nnkExprColonExpr
  349. result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
  350. result = newCall(bindSym("%", brOpen), result)
  351. of nnkCurly: # empty object
  352. x.expectLen(0)
  353. result = newCall(bindSym"newJObject")
  354. of nnkNilLit:
  355. result = newCall(bindSym"newJNull")
  356. of nnkPar:
  357. if x.len == 1: result = toJson(x[0])
  358. else: result = newCall(bindSym("%", brOpen), x)
  359. else:
  360. result = newCall(bindSym("%", brOpen), x)
  361. macro `%*`*(x: untyped): untyped =
  362. ## Convert an expression to a JsonNode directly, without having to specify
  363. ## `%` for every element.
  364. result = toJson(x)
  365. proc `==`* (a, b: JsonNode): bool =
  366. ## Check two nodes for equality
  367. if a.isNil:
  368. if b.isNil: return true
  369. return false
  370. elif b.isNil or a.kind != b.kind:
  371. return false
  372. else:
  373. case a.kind
  374. of JString:
  375. result = a.str == b.str
  376. of JInt:
  377. result = a.num == b.num
  378. of JFloat:
  379. result = a.fnum == b.fnum
  380. of JBool:
  381. result = a.bval == b.bval
  382. of JNull:
  383. result = true
  384. of JArray:
  385. result = a.elems == b.elems
  386. of JObject:
  387. # we cannot use OrderedTable's equality here as
  388. # the order does not matter for equality here.
  389. if a.fields.len != b.fields.len: return false
  390. for key, val in a.fields:
  391. if not b.fields.hasKey(key): return false
  392. if b.fields[key] != val: return false
  393. result = true
  394. proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.}
  395. proc hash*(n: JsonNode): Hash =
  396. ## Compute the hash for a JSON node
  397. case n.kind
  398. of JArray:
  399. result = hash(n.elems)
  400. of JObject:
  401. result = hash(n.fields)
  402. of JInt:
  403. result = hash(n.num)
  404. of JFloat:
  405. result = hash(n.fnum)
  406. of JBool:
  407. result = hash(n.bval.int)
  408. of JString:
  409. result = hash(n.str)
  410. of JNull:
  411. result = Hash(0)
  412. proc hash*(n: OrderedTable[string, JsonNode]): Hash =
  413. for key, val in n:
  414. result = result xor (hash(key) !& hash(val))
  415. result = !$result
  416. proc len*(n: JsonNode): int =
  417. ## If `n` is a `JArray`, it returns the number of elements.
  418. ## If `n` is a `JObject`, it returns the number of pairs.
  419. ## Else it returns 0.
  420. case n.kind
  421. of JArray: result = n.elems.len
  422. of JObject: result = n.fields.len
  423. else: discard
  424. proc `[]`*(node: JsonNode, name: string): JsonNode {.inline, deprecatedGet.} =
  425. ## Gets a field from a `JObject`, which must not be nil.
  426. ## If the value at `name` does not exist, raises KeyError.
  427. ##
  428. ## **Note:** The behaviour of this procedure changed in version 0.14.0. To
  429. ## get a list of usages and to restore the old behaviour of this procedure,
  430. ## compile with the ``-d:nimJsonGet`` flag.
  431. assert(not isNil(node))
  432. assert(node.kind == JObject)
  433. when defined(nimJsonGet):
  434. if not node.fields.hasKey(name): return nil
  435. result = node.fields[name]
  436. proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} =
  437. ## Gets the node at `index` in an Array. Result is undefined if `index`
  438. ## is out of bounds, but as long as array bound checks are enabled it will
  439. ## result in an exception.
  440. assert(not isNil(node))
  441. assert(node.kind == JArray)
  442. return node.elems[index]
  443. proc hasKey*(node: JsonNode, key: string): bool =
  444. ## Checks if `key` exists in `node`.
  445. assert(node.kind == JObject)
  446. result = node.fields.hasKey(key)
  447. proc contains*(node: JsonNode, key: string): bool =
  448. ## Checks if `key` exists in `node`.
  449. assert(node.kind == JObject)
  450. node.fields.hasKey(key)
  451. proc contains*(node: JsonNode, val: JsonNode): bool =
  452. ## Checks if `val` exists in array `node`.
  453. assert(node.kind == JArray)
  454. find(node.elems, val) >= 0
  455. proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use hasKey instead".} = node.hasKey(key)
  456. ## **Deprecated:** use `hasKey` instead.
  457. proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
  458. ## Sets a field from a `JObject`.
  459. assert(obj.kind == JObject)
  460. obj.fields[key] = val
  461. proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
  462. ## Traverses the node and gets the given value. If any of the
  463. ## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the
  464. ## intermediate data structures is not an object.
  465. ##
  466. ## This proc can be used to create tree structures on the
  467. ## fly (sometimes called `autovivification`:idx:):
  468. ##
  469. ## .. code-block:: nim
  470. ## myjson{"parent", "child", "grandchild"} = newJInt(1)
  471. ##
  472. result = node
  473. for key in keys:
  474. if isNil(result) or result.kind != JObject:
  475. return nil
  476. result = result.fields.getOrDefault(key)
  477. proc `{}`*(node: JsonNode, index: varargs[int]): JsonNode =
  478. ## Traverses the node and gets the given value. If any of the
  479. ## indexes do not exist, returns ``nil``. Also returns ``nil`` if one of the
  480. ## intermediate data structures is not an array.
  481. result = node
  482. for i in index:
  483. if isNil(result) or result.kind != JArray or i >= node.len:
  484. return nil
  485. result = result.elems[i]
  486. proc getOrDefault*(node: JsonNode, key: string): JsonNode =
  487. ## Gets a field from a `node`. If `node` is nil or not an object or
  488. ## value at `key` does not exist, returns nil
  489. if not isNil(node) and node.kind == JObject:
  490. result = node.fields.getOrDefault(key)
  491. template simpleGetOrDefault*{`{}`(node, [key])}(node: JsonNode, key: string): JsonNode = node.getOrDefault(key)
  492. proc `{}=`*(node: JsonNode, keys: varargs[string], value: JsonNode) =
  493. ## Traverses the node and tries to set the value at the given location
  494. ## to ``value``. If any of the keys are missing, they are added.
  495. var node = node
  496. for i in 0..(keys.len-2):
  497. if not node.hasKey(keys[i]):
  498. node[keys[i]] = newJObject()
  499. node = node[keys[i]]
  500. node[keys[keys.len-1]] = value
  501. proc delete*(obj: JsonNode, key: string) =
  502. ## Deletes ``obj[key]``.
  503. assert(obj.kind == JObject)
  504. if not obj.fields.hasKey(key):
  505. raise newException(KeyError, "key not in object")
  506. obj.fields.del(key)
  507. proc copy*(p: JsonNode): JsonNode =
  508. ## Performs a deep copy of `a`.
  509. case p.kind
  510. of JString:
  511. result = newJString(p.str)
  512. of JInt:
  513. result = newJInt(p.num)
  514. of JFloat:
  515. result = newJFloat(p.fnum)
  516. of JBool:
  517. result = newJBool(p.bval)
  518. of JNull:
  519. result = newJNull()
  520. of JObject:
  521. result = newJObject()
  522. for key, val in pairs(p.fields):
  523. result.fields[key] = copy(val)
  524. of JArray:
  525. result = newJArray()
  526. for i in items(p.elems):
  527. result.elems.add(copy(i))
  528. # ------------- pretty printing ----------------------------------------------
  529. proc indent(s: var string, i: int) =
  530. s.add(spaces(i))
  531. proc newIndent(curr, indent: int, ml: bool): int =
  532. if ml: return curr + indent
  533. else: return indent
  534. proc nl(s: var string, ml: bool) =
  535. s.add(if ml: "\n" else: " ")
  536. proc escapeJsonUnquoted*(s: string; result: var string) =
  537. ## Converts a string `s` to its JSON representation without quotes.
  538. ## Appends to ``result``.
  539. for c in s:
  540. case c
  541. of '\L': result.add("\\n")
  542. of '\b': result.add("\\b")
  543. of '\f': result.add("\\f")
  544. of '\t': result.add("\\t")
  545. of '\r': result.add("\\r")
  546. of '"': result.add("\\\"")
  547. of '\0'..'\7': result.add("\\u000" & $ord(c))
  548. of '\14'..'\31': result.add("\\u00" & $ord(c))
  549. of '\\': result.add("\\\\")
  550. else: result.add(c)
  551. proc escapeJsonUnquoted*(s: string): string =
  552. ## Converts a string `s` to its JSON representation without quotes.
  553. result = newStringOfCap(s.len + s.len shr 3)
  554. escapeJsonUnquoted(s, result)
  555. proc escapeJson*(s: string; result: var string) =
  556. ## Converts a string `s` to its JSON representation with quotes.
  557. ## Appends to ``result``.
  558. result.add("\"")
  559. escapeJsonUnquoted(s, result)
  560. result.add("\"")
  561. proc escapeJson*(s: string): string =
  562. ## Converts a string `s` to its JSON representation with quotes.
  563. result = newStringOfCap(s.len + s.len shr 3)
  564. escapeJson(s, result)
  565. proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true,
  566. lstArr = false, currIndent = 0) =
  567. case node.kind
  568. of JObject:
  569. if lstArr: result.indent(currIndent) # Indentation
  570. if node.fields.len > 0:
  571. result.add("{")
  572. result.nl(ml) # New line
  573. var i = 0
  574. for key, val in pairs(node.fields):
  575. if i > 0:
  576. result.add(",")
  577. result.nl(ml) # New Line
  578. inc i
  579. # Need to indent more than {
  580. result.indent(newIndent(currIndent, indent, ml))
  581. escapeJson(key, result)
  582. result.add(": ")
  583. toPretty(result, val, indent, ml, false,
  584. newIndent(currIndent, indent, ml))
  585. result.nl(ml)
  586. result.indent(currIndent) # indent the same as {
  587. result.add("}")
  588. else:
  589. result.add("{}")
  590. of JString:
  591. if lstArr: result.indent(currIndent)
  592. escapeJson(node.str, result)
  593. of JInt:
  594. if lstArr: result.indent(currIndent)
  595. when defined(js): result.add($node.num)
  596. else: result.add(node.num)
  597. of JFloat:
  598. if lstArr: result.indent(currIndent)
  599. # Fixme: implement new system.add ops for the JS target
  600. when defined(js): result.add($node.fnum)
  601. else: result.add(node.fnum)
  602. of JBool:
  603. if lstArr: result.indent(currIndent)
  604. result.add(if node.bval: "true" else: "false")
  605. of JArray:
  606. if lstArr: result.indent(currIndent)
  607. if len(node.elems) != 0:
  608. result.add("[")
  609. result.nl(ml)
  610. for i in 0..len(node.elems)-1:
  611. if i > 0:
  612. result.add(",")
  613. result.nl(ml) # New Line
  614. toPretty(result, node.elems[i], indent, ml,
  615. true, newIndent(currIndent, indent, ml))
  616. result.nl(ml)
  617. result.indent(currIndent)
  618. result.add("]")
  619. else: result.add("[]")
  620. of JNull:
  621. if lstArr: result.indent(currIndent)
  622. result.add("null")
  623. proc pretty*(node: JsonNode, indent = 2): string =
  624. ## Returns a JSON Representation of `node`, with indentation and
  625. ## on multiple lines.
  626. result = ""
  627. toPretty(result, node, indent)
  628. proc toUgly*(result: var string, node: JsonNode) =
  629. ## Converts `node` to its JSON Representation, without
  630. ## regard for human readability. Meant to improve ``$`` string
  631. ## conversion performance.
  632. ##
  633. ## JSON representation is stored in the passed `result`
  634. ##
  635. ## This provides higher efficiency than the ``pretty`` procedure as it
  636. ## does **not** attempt to format the resulting JSON to make it human readable.
  637. var comma = false
  638. case node.kind:
  639. of JArray:
  640. result.add "["
  641. for child in node.elems:
  642. if comma: result.add ","
  643. else: comma = true
  644. result.toUgly child
  645. result.add "]"
  646. of JObject:
  647. result.add "{"
  648. for key, value in pairs(node.fields):
  649. if comma: result.add ","
  650. else: comma = true
  651. key.escapeJson(result)
  652. result.add ":"
  653. result.toUgly value
  654. result.add "}"
  655. of JString:
  656. node.str.escapeJson(result)
  657. of JInt:
  658. when defined(js): result.add($node.num)
  659. else: result.add(node.num)
  660. of JFloat:
  661. when defined(js): result.add($node.fnum)
  662. else: result.add(node.fnum)
  663. of JBool:
  664. result.add(if node.bval: "true" else: "false")
  665. of JNull:
  666. result.add "null"
  667. proc `$`*(node: JsonNode): string =
  668. ## Converts `node` to its JSON Representation on one line.
  669. result = newStringOfCap(node.len shl 1)
  670. toUgly(result, node)
  671. iterator items*(node: JsonNode): JsonNode =
  672. ## Iterator for the items of `node`. `node` has to be a JArray.
  673. assert node.kind == JArray
  674. for i in items(node.elems):
  675. yield i
  676. iterator mitems*(node: var JsonNode): var JsonNode =
  677. ## Iterator for the items of `node`. `node` has to be a JArray. Items can be
  678. ## modified.
  679. assert node.kind == JArray
  680. for i in mitems(node.elems):
  681. yield i
  682. iterator pairs*(node: JsonNode): tuple[key: string, val: JsonNode] =
  683. ## Iterator for the child elements of `node`. `node` has to be a JObject.
  684. assert node.kind == JObject
  685. for key, val in pairs(node.fields):
  686. yield (key, val)
  687. iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] =
  688. ## Iterator for the child elements of `node`. `node` has to be a JObject.
  689. ## Values can be modified
  690. assert node.kind == JObject
  691. for key, val in mpairs(node.fields):
  692. yield (key, val)
  693. proc parseJson(p: var JsonParser): JsonNode =
  694. ## Parses JSON from a JSON Parser `p`.
  695. case p.tok
  696. of tkString:
  697. # we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
  698. result = newJStringMove(p.a)
  699. p.a = ""
  700. discard getTok(p)
  701. of tkInt:
  702. result = newJInt(parseBiggestInt(p.a))
  703. discard getTok(p)
  704. of tkFloat:
  705. result = newJFloat(parseFloat(p.a))
  706. discard getTok(p)
  707. of tkTrue:
  708. result = newJBool(true)
  709. discard getTok(p)
  710. of tkFalse:
  711. result = newJBool(false)
  712. discard getTok(p)
  713. of tkNull:
  714. result = newJNull()
  715. discard getTok(p)
  716. of tkCurlyLe:
  717. result = newJObject()
  718. discard getTok(p)
  719. while p.tok != tkCurlyRi:
  720. if p.tok != tkString:
  721. raiseParseErr(p, "string literal as key")
  722. var key = p.a
  723. discard getTok(p)
  724. eat(p, tkColon)
  725. var val = parseJson(p)
  726. result[key] = val
  727. if p.tok != tkComma: break
  728. discard getTok(p)
  729. eat(p, tkCurlyRi)
  730. of tkBracketLe:
  731. result = newJArray()
  732. discard getTok(p)
  733. while p.tok != tkBracketRi:
  734. result.add(parseJson(p))
  735. if p.tok != tkComma: break
  736. discard getTok(p)
  737. eat(p, tkBracketRi)
  738. of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof:
  739. raiseParseErr(p, "{")
  740. when not defined(js):
  741. proc parseJson*(s: Stream, filename: string = ""): JsonNode =
  742. ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
  743. ## for nice error messages.
  744. ## If `s` contains extra data, it will raise `JsonParsingError`.
  745. var p: JsonParser
  746. p.open(s, filename)
  747. try:
  748. discard getTok(p) # read first token
  749. result = p.parseJson()
  750. eat(p, tkEof) # check if there is no extra data
  751. finally:
  752. p.close()
  753. proc parseJson*(buffer: string): JsonNode =
  754. ## Parses JSON from `buffer`.
  755. ## If `buffer` contains extra data, it will raise `JsonParsingError`.
  756. result = parseJson(newStringStream(buffer), "input")
  757. proc parseFile*(filename: string): JsonNode =
  758. ## Parses `file` into a `JsonNode`.
  759. ## If `file` contains extra data, it will raise `JsonParsingError`.
  760. var stream = newFileStream(filename, fmRead)
  761. if stream == nil:
  762. raise newException(IOError, "cannot read from file: " & filename)
  763. result = parseJson(stream, filename)
  764. else:
  765. from math import `mod`
  766. type
  767. JSObject = object
  768. proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
  769. proc getVarType(x: JSObject): JsonNodeKind =
  770. result = JNull
  771. proc getProtoName(y: JSObject): cstring
  772. {.importc: "Object.prototype.toString.call".}
  773. case $getProtoName(x) # TODO: Implicit returns fail here.
  774. of "[object Array]": return JArray
  775. of "[object Object]": return JObject
  776. of "[object Number]":
  777. if cast[float](x) mod 1.0 == 0:
  778. return JInt
  779. else:
  780. return JFloat
  781. of "[object Boolean]": return JBool
  782. of "[object Null]": return JNull
  783. of "[object String]": return JString
  784. else: assert false
  785. proc len(x: JSObject): int =
  786. assert x.getVarType == JArray
  787. asm """
  788. `result` = `x`.length;
  789. """
  790. proc `[]`(x: JSObject, y: string): JSObject =
  791. assert x.getVarType == JObject
  792. asm """
  793. `result` = `x`[`y`];
  794. """
  795. proc `[]`(x: JSObject, y: int): JSObject =
  796. assert x.getVarType == JArray
  797. asm """
  798. `result` = `x`[`y`];
  799. """
  800. proc convertObject(x: JSObject): JsonNode =
  801. case getVarType(x)
  802. of JArray:
  803. result = newJArray()
  804. for i in 0 ..< x.len:
  805. result.add(x[i].convertObject())
  806. of JObject:
  807. result = newJObject()
  808. asm """for (var property in `x`) {
  809. if (`x`.hasOwnProperty(property)) {
  810. """
  811. var nimProperty: cstring
  812. var nimValue: JSObject
  813. asm "`nimProperty` = property; `nimValue` = `x`[property];"
  814. result[$nimProperty] = nimValue.convertObject()
  815. asm "}}"
  816. of JInt:
  817. result = newJInt(cast[int](x))
  818. of JFloat:
  819. result = newJFloat(cast[float](x))
  820. of JString:
  821. result = newJString($cast[cstring](x))
  822. of JBool:
  823. result = newJBool(cast[bool](x))
  824. of JNull:
  825. result = newJNull()
  826. proc parseJson*(buffer: string): JsonNode =
  827. return parseNativeJson(buffer).convertObject()
  828. # -- Json deserialiser macro. --
  829. proc createJsonIndexer(jsonNode: NimNode,
  830. index: string | int | NimNode): NimNode
  831. {.compileTime.} =
  832. when index is string:
  833. let indexNode = newStrLitNode(index)
  834. elif index is int:
  835. let indexNode = newIntLitNode(index)
  836. elif index is NimNode:
  837. let indexNode = index
  838. result = newNimNode(nnkBracketExpr).add(
  839. jsonNode,
  840. indexNode
  841. )
  842. proc transformJsonIndexer(jsonNode: NimNode): NimNode =
  843. case jsonNode.kind
  844. of nnkBracketExpr:
  845. result = newNimNode(nnkCurlyExpr)
  846. else:
  847. result = jsonNode.copy()
  848. for child in jsonNode:
  849. result.add(transformJsonIndexer(child))
  850. template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
  851. ast: string) =
  852. if node.kind notin kinds:
  853. let msg = "Incorrect JSON kind. Wanted '$1' in '$2' but got '$3'." % [
  854. $kinds,
  855. ast,
  856. $node.kind
  857. ]
  858. raise newException(JsonKindError, msg)
  859. proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
  860. when T is SomeInteger:
  861. # TODO: I shouldn't need this proc.
  862. proc convert[T](x: BiggestInt): T = T(x)
  863. verifyJsonKind(node, {JInt}, ast)
  864. return convert[T](node.getBiggestInt())
  865. else:
  866. verifyJsonKind(node, {JString}, ast)
  867. return parseEnum[T](node.getStr())
  868. proc toIdentNode(typeNode: NimNode): NimNode =
  869. ## Converts a Sym type node (returned by getType et al.) into an
  870. ## Ident node. Placing Sym type nodes inside the resulting code AST is
  871. ## unsound (according to @Araq) so this is necessary.
  872. case typeNode.kind
  873. of nnkSym:
  874. return newIdentNode($typeNode)
  875. of nnkBracketExpr:
  876. result = typeNode
  877. for i in 0..<len(result):
  878. result[i] = newIdentNode($result[i])
  879. of nnkIdent:
  880. return typeNode
  881. else:
  882. doAssert false, "Cannot convert typeNode to an ident node: " & $typeNode.kind
  883. proc createGetEnumCall(jsonNode, kindType: NimNode): NimNode =
  884. # -> getEnum(`jsonNode`, `kindType`)
  885. let getEnumSym = bindSym("getEnum")
  886. let astStrLit = toStrLit(jsonNode)
  887. let getEnumCall = newCall(getEnumSym, jsonNode, astStrLit, kindType)
  888. return getEnumCall
  889. proc createOfBranchCond(ofBranch, getEnumCall: NimNode): NimNode =
  890. ## Creates an expression that acts as the condition for an ``of`` branch.
  891. var cond = newIdentNode("false")
  892. for ofCond in ofBranch:
  893. if ofCond.kind == nnkRecList:
  894. break
  895. let comparison = infix(getEnumCall, "==", ofCond)
  896. cond = infix(cond, "or", comparison)
  897. return cond
  898. proc processObjField(field, jsonNode: NimNode): seq[NimNode] {.compileTime.}
  899. proc processOfBranch(ofBranch, jsonNode, kindType,
  900. kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
  901. ## Processes each field inside of an object's ``of`` branch.
  902. ## For each field a new ExprColonExpr node is created and put in the
  903. ## resulting list.
  904. ##
  905. ## Sample ``ofBranch`` AST:
  906. ##
  907. ## .. code-block::plain
  908. ## OfBranch of 0, 1:
  909. ## IntLit 0 foodPos: float
  910. ## IntLit 1 enemyPos: float
  911. ## RecList
  912. ## Sym "foodPos"
  913. ## Sym "enemyPos"
  914. result = @[]
  915. let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
  916. for branchField in ofBranch[^1]:
  917. let objFields = processObjField(branchField, jsonNode)
  918. for objField in objFields:
  919. let exprColonExpr = newNimNode(nnkExprColonExpr)
  920. result.add(exprColonExpr)
  921. # Add the name of the field.
  922. exprColonExpr.add(toIdentNode(objField[0]))
  923. # Add the value of the field.
  924. let cond = createOfBranchCond(ofBranch, getEnumCall)
  925. exprColonExpr.add(newIfStmt(
  926. (cond, objField[1])
  927. ))
  928. proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
  929. kindJsonNode: NimNode): seq[NimNode] {.compileTime.} =
  930. ## Processes each field inside of a variant object's ``else`` branch.
  931. ##
  932. ## ..code-block::plain
  933. ## Else
  934. ## RecList
  935. ## Sym "other"
  936. result = @[]
  937. let getEnumCall = createGetEnumCall(kindJsonNode, kindType)
  938. # We need to build up a list of conditions from each ``of`` branch so that
  939. # we can then negate it to get ``else``.
  940. var cond = newIdentNode("false")
  941. for i in 1 ..< len(recCaseNode):
  942. if recCaseNode[i].kind == nnkElse:
  943. break
  944. cond = infix(cond, "or", createOfBranchCond(recCaseNode[i], getEnumCall))
  945. # Negate the condition.
  946. cond = prefix(cond, "not")
  947. for branchField in elseBranch[^1]:
  948. let objFields = processObjField(branchField, jsonNode)
  949. for objField in objFields:
  950. let exprColonExpr = newNimNode(nnkExprColonExpr)
  951. result.add(exprColonExpr)
  952. # Add the name of the field.
  953. exprColonExpr.add(toIdentNode(objField[0]))
  954. # Add the value of the field.
  955. let ifStmt = newIfStmt((cond, objField[1]))
  956. exprColonExpr.add(ifStmt)
  957. proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
  958. proc detectDistinctType(typeSym: NimNode): NimNode =
  959. let
  960. typeImpl = getTypeImpl(typeSym)
  961. typeInst = getTypeInst(typeSym)
  962. result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
  963. proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
  964. ## Process a field from a ``RecList``.
  965. ##
  966. ## The field will typically be a simple ``Sym`` node, but for object variants
  967. ## it may also be a ``RecCase`` in which case things become complicated.
  968. result = @[]
  969. case field.kind
  970. of nnkSym:
  971. # Ordinary field. For example, `name: string`.
  972. let exprColonExpr = newNimNode(nnkExprColonExpr)
  973. result.add(exprColonExpr)
  974. # Add the field name.
  975. exprColonExpr.add(toIdentNode(field))
  976. # Add the field value.
  977. # -> jsonNode["`field`"]
  978. let indexedJsonNode = createJsonIndexer(jsonNode, $field)
  979. let typeNode = detectDistinctType(field)
  980. exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
  981. of nnkRecCase:
  982. # A "case" field that introduces a variant.
  983. let exprColonExpr = newNimNode(nnkExprColonExpr)
  984. result.add(exprColonExpr)
  985. # Add the "case" field name (usually "kind").
  986. exprColonExpr.add(toIdentNode(field[0]))
  987. # -> jsonNode["`field[0]`"]
  988. let kindJsonNode = createJsonIndexer(jsonNode, $field[0])
  989. # Add the "case" field's value.
  990. let kindType = toIdentNode(getTypeInst(field[0]))
  991. let getEnumSym = bindSym("getEnum")
  992. let astStrLit = toStrLit(kindJsonNode)
  993. let getEnumCall = newCall(getEnumSym, kindJsonNode, astStrLit, kindType)
  994. exprColonExpr.add(getEnumCall)
  995. # Iterate through each `of` branch.
  996. for i in 1 ..< field.len:
  997. case field[i].kind
  998. of nnkOfBranch:
  999. result.add processOfBranch(field[i], jsonNode, kindType, kindJsonNode)
  1000. of nnkElse:
  1001. result.add processElseBranch(field, field[i], jsonNode, kindType, kindJsonNode)
  1002. else:
  1003. doAssert false, "Expected OfBranch or Else node kinds, got: " & $field[i].kind
  1004. else:
  1005. doAssert false, "Unable to process object field: " & $field.kind
  1006. doAssert result.len > 0
  1007. proc processFields(obj: NimNode,
  1008. jsonNode: NimNode): seq[NimNode] {.compileTime.} =
  1009. ## Process all the fields of an ``ObjectTy`` and any of its
  1010. ## parent type's fields (via inheritance).
  1011. result = @[]
  1012. case obj.kind
  1013. of nnkObjectTy:
  1014. expectKind(obj[2], nnkRecList)
  1015. for field in obj[2]:
  1016. let nodes = processObjField(field, jsonNode)
  1017. result.add(nodes)
  1018. # process parent type fields
  1019. case obj[1].kind
  1020. of nnkBracketExpr:
  1021. assert $obj[1][0] == "ref"
  1022. result.add(processFields(getType(obj[1][1]), jsonNode))
  1023. of nnkSym:
  1024. result.add(processFields(getType(obj[1]), jsonNode))
  1025. else:
  1026. discard
  1027. of nnkTupleTy:
  1028. for identDefs in obj:
  1029. expectKind(identDefs, nnkIdentDefs)
  1030. let nodes = processObjField(identDefs[0], jsonNode)
  1031. result.add(nodes)
  1032. else:
  1033. doAssert false, "Unable to process field type: " & $obj.kind
  1034. proc processType(typeName: NimNode, obj: NimNode,
  1035. jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
  1036. ## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
  1037. ##
  1038. ## Sample ``ObjectTy``:
  1039. ##
  1040. ## .. code-block::plain
  1041. ## ObjectTy
  1042. ## Empty
  1043. ## InheritanceInformation
  1044. ## RecList
  1045. ## Sym "events"
  1046. case obj.kind
  1047. of nnkObjectTy, nnkTupleTy:
  1048. # Create object constructor.
  1049. result =
  1050. if obj.kind == nnkObjectTy: newNimNode(nnkObjConstr)
  1051. else: newNimNode(nnkPar)
  1052. if obj.kind == nnkObjectTy:
  1053. result.add(typeName) # Name of the type to construct.
  1054. # Process each object/tuple field and add it as an exprColonExpr
  1055. result.add(processFields(obj, jsonNode))
  1056. # Object might be null. So we need to check for that.
  1057. if isRef:
  1058. result = quote do:
  1059. verifyJsonKind(`jsonNode`, {JObject, JNull}, astToStr(`jsonNode`))
  1060. if `jsonNode`.kind == JNull:
  1061. nil
  1062. else:
  1063. `result`
  1064. else:
  1065. result = quote do:
  1066. verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
  1067. `result`
  1068. of nnkEnumTy:
  1069. let instType = toIdentNode(getTypeInst(typeName))
  1070. let getEnumCall = createGetEnumCall(jsonNode, instType)
  1071. result = quote do:
  1072. (
  1073. `getEnumCall`
  1074. )
  1075. of nnkSym:
  1076. let name = normalize($typeName.getTypeImpl())
  1077. case name
  1078. of "string":
  1079. result = quote do:
  1080. (
  1081. verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
  1082. if `jsonNode`.kind == JNull: "" else: `jsonNode`.str
  1083. )
  1084. of "biggestint":
  1085. result = quote do:
  1086. (
  1087. verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
  1088. `jsonNode`.num
  1089. )
  1090. of "bool":
  1091. result = quote do:
  1092. (
  1093. verifyJsonKind(`jsonNode`, {JBool}, astToStr(`jsonNode`));
  1094. `jsonNode`.bval
  1095. )
  1096. else:
  1097. if name.startsWith("int") or name.startsWith("uint"):
  1098. result = quote do:
  1099. (
  1100. verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
  1101. `jsonNode`.num.`obj`
  1102. )
  1103. elif name.startsWith("float"):
  1104. result = quote do:
  1105. (
  1106. verifyJsonKind(`jsonNode`, {JInt, JFloat}, astToStr(`jsonNode`));
  1107. if `jsonNode`.kind == JFloat: `jsonNode`.fnum.`obj` else: `jsonNode`.num.`obj`
  1108. )
  1109. else:
  1110. doAssert false, "Unable to process nnkSym " & $typeName
  1111. else:
  1112. doAssert false, "Unable to process type: " & $obj.kind
  1113. doAssert(not result.isNil(), "processType not initialised.")
  1114. import options
  1115. proc workaroundMacroNone[T](): Option[T] =
  1116. none(T)
  1117. proc depth(n: NimNode, current = 0): int =
  1118. result = 1
  1119. for child in n:
  1120. let d = 1 + child.depth(current + 1)
  1121. if d > result:
  1122. result = d
  1123. proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
  1124. ## Accepts a type description, i.e. "ref Type", "seq[Type]", "Type" etc.
  1125. ##
  1126. ## The ``jsonNode`` refers to the node variable that we are deserialising.
  1127. ##
  1128. ## Returns an object constructor node.
  1129. # echo("--createConsuctor-- \n", treeRepr(typeSym))
  1130. # echo()
  1131. if depth(jsonNode) > 150:
  1132. error("The `to` macro does not support ref objects with cycles.", jsonNode)
  1133. case typeSym.kind
  1134. of nnkBracketExpr:
  1135. var bracketName = ($typeSym[0]).normalize
  1136. case bracketName
  1137. of "option":
  1138. # TODO: Would be good to verify that this is Option[T] from
  1139. # options module I suppose.
  1140. let lenientJsonNode = transformJsonIndexer(jsonNode)
  1141. let optionGeneric = typeSym[1]
  1142. let value = createConstructor(typeSym[1], jsonNode)
  1143. let workaround = bindSym("workaroundMacroNone") # TODO: Nim Bug: This shouldn't be necessary.
  1144. result = quote do:
  1145. (
  1146. if `lenientJsonNode`.isNil or `jsonNode`.kind == JNull: `workaround`[`optionGeneric`]() else: some[`optionGeneric`](`value`)
  1147. )
  1148. of "table", "orderedtable":
  1149. let tableKeyType = typeSym[1]
  1150. if ($tableKeyType).cmpIgnoreStyle("string") != 0:
  1151. error("JSON doesn't support keys of type " & $tableKeyType)
  1152. let tableValueType = typeSym[2]
  1153. let forLoopKey = genSym(nskForVar, "key")
  1154. let indexerNode = createJsonIndexer(jsonNode, forLoopKey)
  1155. let constructorNode = createConstructor(tableValueType, indexerNode)
  1156. let tableInit =
  1157. if bracketName == "table":
  1158. bindSym("initTable")
  1159. else:
  1160. bindSym("initOrderedTable")
  1161. # Create a statement expression containing a for loop.
  1162. result = quote do:
  1163. (
  1164. var map = `tableInit`[`tableKeyType`, `tableValueType`]();
  1165. verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
  1166. for `forLoopKey` in keys(`jsonNode`.fields): map[`forLoopKey`] = `constructorNode`;
  1167. map
  1168. )
  1169. of "ref":
  1170. # Ref type.
  1171. var typeName = $typeSym[1]
  1172. # Remove the `:ObjectType` suffix.
  1173. if typeName.endsWith(":ObjectType"):
  1174. typeName = typeName[0 .. ^12]
  1175. let obj = getType(typeSym[1])
  1176. result = processType(newIdentNode(typeName), obj, jsonNode, true)
  1177. of "seq":
  1178. let seqT = typeSym[1]
  1179. let forLoopI = genSym(nskForVar, "i")
  1180. let indexerNode = createJsonIndexer(jsonNode, forLoopI)
  1181. let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
  1182. # Create a statement expression containing a for loop.
  1183. result = quote do:
  1184. (
  1185. var list: `typeSym` = @[];
  1186. verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
  1187. for `forLoopI` in 0 ..< `jsonNode`.len: list.add(`constructorNode`);
  1188. list
  1189. )
  1190. of "array":
  1191. let arrayT = typeSym[2]
  1192. let forLoopI = genSym(nskForVar, "i")
  1193. let indexerNode = createJsonIndexer(jsonNode, forLoopI)
  1194. let constructorNode = createConstructor(arrayT, indexerNode)
  1195. # Create a statement expression containing a for loop.
  1196. result = quote do:
  1197. (
  1198. var list: `typeSym`;
  1199. verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
  1200. for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`;
  1201. list
  1202. )
  1203. else:
  1204. # Generic type.
  1205. let obj = getType(typeSym)
  1206. result = processType(typeSym, obj, jsonNode, false)
  1207. of nnkSym:
  1208. # Handle JsonNode.
  1209. if ($typeSym).cmpIgnoreStyle("jsonnode") == 0:
  1210. return jsonNode
  1211. # Handle all other types.
  1212. let obj = getType(typeSym)
  1213. let typeNode = getTypeImpl(typeSym)
  1214. if typeNode.typeKind == ntyDistinct:
  1215. result = createConstructor(typeNode, jsonNode)
  1216. elif obj.kind == nnkBracketExpr:
  1217. # When `Sym "Foo"` turns out to be a `ref object`.
  1218. result = createConstructor(obj, jsonNode)
  1219. else:
  1220. result = processType(typeSym, obj, jsonNode, false)
  1221. of nnkTupleTy:
  1222. result = processType(typeSym, typeSym, jsonNode, false)
  1223. of nnkPar, nnkTupleConstr:
  1224. # TODO: The fact that `jsonNode` here works to give a good line number
  1225. # is weird. Specifying typeSym should work but doesn't.
  1226. error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
  1227. of nnkDistinctTy:
  1228. var baseType = typeSym
  1229. # solve nested distinct types
  1230. while baseType.typeKind == ntyDistinct:
  1231. let impl = getTypeImpl(baseType[0])
  1232. if impl.typeKind != ntyDistinct:
  1233. baseType = baseType[0]
  1234. break
  1235. baseType = impl
  1236. let ret = createConstructor(baseType, jsonNode)
  1237. let typeInst = getTypeInst(typeSym)
  1238. result = quote do:
  1239. (
  1240. `typeInst`(`ret`)
  1241. )
  1242. else:
  1243. doAssert false, "Unable to create constructor for: " & $typeSym.kind
  1244. doAssert(not result.isNil(), "Constructor not initialised.")
  1245. proc postProcess(node: NimNode): NimNode
  1246. proc postProcessValue(value: NimNode): NimNode =
  1247. ## Looks for object constructors and calls the ``postProcess`` procedure
  1248. ## on them. Otherwise it just returns the node as-is.
  1249. case value.kind
  1250. of nnkObjConstr:
  1251. result = postProcess(value)
  1252. else:
  1253. result = value
  1254. for i in 0 ..< len(result):
  1255. result[i] = postProcessValue(result[i])
  1256. proc postProcessExprColonExpr(exprColonExpr, resIdent: NimNode): NimNode =
  1257. ## Transform each field mapping in the ExprColonExpr into a simple
  1258. ## field assignment. Special processing is performed if the field mapping
  1259. ## has an if statement.
  1260. ##
  1261. ## ..code-block::plain
  1262. ## field: (if true: 12) -> if true: `resIdent`.field = 12
  1263. expectKind(exprColonExpr, nnkExprColonExpr)
  1264. let fieldName = exprColonExpr[0]
  1265. let fieldValue = exprColonExpr[1]
  1266. case fieldValue.kind
  1267. of nnkIfStmt:
  1268. doAssert fieldValue.len == 1, "Cannot postProcess two ElifBranches."
  1269. expectKind(fieldValue[0], nnkElifBranch)
  1270. let cond = fieldValue[0][0]
  1271. let bodyValue = postProcessValue(fieldValue[0][1])
  1272. doAssert(bodyValue.kind != nnkNilLit)
  1273. result =
  1274. quote do:
  1275. if `cond`:
  1276. `resIdent`.`fieldName` = `bodyValue`
  1277. else:
  1278. let fieldValue = postProcessValue(fieldValue)
  1279. doAssert(fieldValue.kind != nnkNilLit)
  1280. result =
  1281. quote do:
  1282. `resIdent`.`fieldName` = `fieldValue`
  1283. proc postProcess(node: NimNode): NimNode =
  1284. ## The ``createConstructor`` proc creates a ObjConstr node which contains
  1285. ## if statements for fields that may not be assignable (due to an object
  1286. ## variant). Nim doesn't handle this, but may do in the future.
  1287. ##
  1288. ## For simplicity, we post process the object constructor into multiple
  1289. ## assignments.
  1290. ##
  1291. ## For example:
  1292. ##
  1293. ## ..code-block::plain
  1294. ## Object( (var res = Object();
  1295. ## field: if true: 12 -> if true: res.field = 12;
  1296. ## ) res)
  1297. result = newNimNode(nnkStmtListExpr)
  1298. expectKind(node, nnkObjConstr)
  1299. # Create the type.
  1300. # -> var res = Object()
  1301. var resIdent = genSym(nskVar, "res")
  1302. # TODO: Placing `node[0]` inside quote is buggy
  1303. var resType = toIdentNode(node[0])
  1304. result.add(
  1305. quote do:
  1306. var `resIdent` = `resType`();
  1307. )
  1308. # Process each ExprColonExpr.
  1309. for i in 1..<len(node):
  1310. result.add postProcessExprColonExpr(node[i], resIdent)
  1311. # Return the `res` variable.
  1312. result.add(
  1313. quote do:
  1314. `resIdent`
  1315. )
  1316. macro to*(node: JsonNode, T: typedesc): untyped =
  1317. ## `Unmarshals`:idx: the specified node into the object type specified.
  1318. ##
  1319. ## Known limitations:
  1320. ##
  1321. ## * Heterogeneous arrays are not supported.
  1322. ## * Sets in object variants are not supported.
  1323. ## * Not nil annotations are not supported.
  1324. ##
  1325. ## Example:
  1326. ##
  1327. ## .. code-block:: Nim
  1328. ## let jsonNode = parseJson("""
  1329. ## {
  1330. ## "person": {
  1331. ## "name": "Nimmer",
  1332. ## "age": 21
  1333. ## },
  1334. ## "list": [1, 2, 3, 4]
  1335. ## }
  1336. ## """)
  1337. ##
  1338. ## type
  1339. ## Person = object
  1340. ## name: string
  1341. ## age: int
  1342. ##
  1343. ## Data = object
  1344. ## person: Person
  1345. ## list: seq[int]
  1346. ##
  1347. ## var data = to(jsonNode, Data)
  1348. ## doAssert data.person.name == "Nimmer"
  1349. ## doAssert data.person.age == 21
  1350. ## doAssert data.list == @[1, 2, 3, 4]
  1351. let typeNode = getTypeImpl(T)
  1352. expectKind(typeNode, nnkBracketExpr)
  1353. doAssert(($typeNode[0]).normalize == "typedesc")
  1354. # Create `temp` variable to store the result in case the user calls this
  1355. # on `parseJson` (see bug #6604).
  1356. result = newNimNode(nnkStmtListExpr)
  1357. let temp = genSym(nskLet, "temp")
  1358. result.add quote do:
  1359. let `temp` = `node`
  1360. let constructor = createConstructor(typeNode[1], temp)
  1361. # TODO: Rename postProcessValue and move it (?)
  1362. result.add(postProcessValue(constructor))
  1363. # echo(treeRepr(result))
  1364. # echo(toStrLit(result))
  1365. when false:
  1366. import os
  1367. var s = newFileStream(paramStr(1), fmRead)
  1368. if s == nil: quit("cannot open the file" & paramStr(1))
  1369. var x: JsonParser
  1370. open(x, s, paramStr(1))
  1371. while true:
  1372. next(x)
  1373. case x.kind
  1374. of jsonError:
  1375. Echo(x.errorMsg())
  1376. break
  1377. of jsonEof: break
  1378. of jsonString, jsonInt, jsonFloat: echo(x.str)
  1379. of jsonTrue: echo("!TRUE")
  1380. of jsonFalse: echo("!FALSE")
  1381. of jsonNull: echo("!NULL")
  1382. of jsonObjectStart: echo("{")
  1383. of jsonObjectEnd: echo("}")
  1384. of jsonArrayStart: echo("[")
  1385. of jsonArrayEnd: echo("]")
  1386. close(x)
  1387. # { "json": 5 }
  1388. # To get that we shall use, obj["json"]
  1389. when isMainModule:
  1390. # Note: Macro tests are in tests/stdlib/tjsonmacro.nim
  1391. let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}"""
  1392. # nil passthrough
  1393. doAssert(testJson{"doesnt_exist"}{"anything"}.isNil)
  1394. testJson{["e", "f"]} = %true
  1395. doAssert(testJson["e"]["f"].bval)
  1396. # make sure UTF-16 decoding works.
  1397. doAssert(testJson["c"].str == "🎃")
  1398. doAssert(testJson["d"].str == "æ")
  1399. # make sure no memory leek when parsing invalid string
  1400. let startMemory = getOccupiedMem()
  1401. for i in 0 .. 10000:
  1402. try:
  1403. discard parseJson"""{ invalid"""
  1404. except:
  1405. discard
  1406. # memory diff should less than 4M
  1407. doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
  1408. # test `$`
  1409. let stringified = $testJson
  1410. let parsedAgain = parseJson(stringified)
  1411. doAssert(parsedAgain["b"].str == "asd")
  1412. parsedAgain["abc"] = %5
  1413. doAssert parsedAgain["abc"].num == 5
  1414. # Bounds checking
  1415. when compileOption("boundChecks"):
  1416. try:
  1417. let a = testJson["a"][9]
  1418. doAssert(false, "IndexError not thrown")
  1419. except IndexError:
  1420. discard
  1421. try:
  1422. let a = testJson["a"][-1]
  1423. doAssert(false, "IndexError not thrown")
  1424. except IndexError:
  1425. discard
  1426. try:
  1427. doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value")
  1428. except:
  1429. doAssert(false, "IndexError thrown for valid index")
  1430. doAssert(testJson{"b"}.getStr()=="asd", "Couldn't fetch a singly nested key with {}")
  1431. doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil")
  1432. doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
  1433. doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil")
  1434. doAssert(testJson{"a"}==parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found")
  1435. doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil")
  1436. # Generator:
  1437. var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}]
  1438. doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
  1439. var j2 = %*
  1440. [
  1441. {
  1442. "name": "John",
  1443. "age": 30
  1444. },
  1445. {
  1446. "name": "Susan",
  1447. "age": 31
  1448. }
  1449. ]
  1450. doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
  1451. var name = "John"
  1452. let herAge = 30
  1453. const hisAge = 31
  1454. var j3 = %*
  1455. [ { "name": "John"
  1456. , "age": herAge
  1457. }
  1458. , { "name": "Susan"
  1459. , "age": hisAge
  1460. }
  1461. ]
  1462. doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}]
  1463. var j4 = %*{"test": nil}
  1464. doAssert j4 == %{"test": newJNull()}
  1465. let seqOfNodes = @[%1, %2]
  1466. let jSeqOfNodes = %seqOfNodes
  1467. doAssert(jSeqOfNodes[1].num == 2)
  1468. type MyObj = object
  1469. a, b: int
  1470. s: string
  1471. f32: float32
  1472. f64: float64
  1473. next: ref MyObj
  1474. var m: MyObj
  1475. m.s = "hi"
  1476. m.a = 5
  1477. let jMyObj = %m
  1478. doAssert(jMyObj["a"].num == 5)
  1479. doAssert(jMyObj["s"].str == "hi")
  1480. # Test loading of file.
  1481. when not defined(js):
  1482. echo("99% of tests finished. Going to try loading file.")
  1483. var parsed = parseFile("tests/testdata/jsontest.json")
  1484. try:
  1485. discard parsed["key2"][12123]
  1486. doAssert(false)
  1487. except IndexError: doAssert(true)
  1488. var parsed2 = parseFile("tests/testdata/jsontest2.json")
  1489. doAssert(parsed2{"repository", "description"}.str=="IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}")
  1490. doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ"
  1491. doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0020" # for #7887
  1492. doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\""
  1493. doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0020\"" # for #7887
  1494. # Test with extra data
  1495. when not defined(js):
  1496. try:
  1497. discard parseJson("123 456")
  1498. doAssert(false)
  1499. except JsonParsingError:
  1500. doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
  1501. try:
  1502. discard parseFile("tests/testdata/jsonwithextradata.json")
  1503. doAssert(false)
  1504. except JsonParsingError:
  1505. doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
  1506. # bug #6438
  1507. doAssert($ %*[] == "[]")
  1508. doAssert($ %*{} == "{}")
  1509. # bug #9111
  1510. block:
  1511. type
  1512. Bar = string
  1513. Foo = object
  1514. a: int
  1515. b: Bar
  1516. let
  1517. js = """{"a": 123, "b": "abc"}""".parseJson
  1518. foo = js.to Foo
  1519. doAssert(foo.b == "abc")