Source Edit

This module implements a simple high performance JSON parser. JSON (JavaScript Object Notation) is a lightweight data-interchange format that is easy for humans to read and write (unlike XML). It is easy for machines to parse and generate. JSON is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999.

See also

Overview

Parsing JSON

JSON often arrives into your program (via an API or a file) as a string. The first step is to change it from its serialized form into a nested object structure called a JsonNode.

The parseJson procedure takes a string containing JSON and returns a JsonNode object. This is an object variant and it is either a JObject, JArray, JString, JInt, JFloat, JBool or JNull. You check the kind of this object variant by using the kind accessor.

For a JsonNode who’s kind is JObject, you can access its fields using the [] operator. The following example shows how to do this:

  1. import std/json
  2. let jsonNode = parseJson("""{"key": 3.14}""")
  3. doAssert jsonNode.kind == JObject
  4. doAssert jsonNode["key"].kind == JFloat

Reading values

Once you have a JsonNode, retrieving the values can then be achieved by using one of the helper procedures, which include:

  • getInt
  • getFloat
  • getStr
  • getBool

To retrieve the value of “key” you can do the following:

  1. import std/json
  2. let jsonNode = parseJson("""{"key": 3.14}""")
  3. doAssert jsonNode["key"].getFloat() == 3.14

Important: The [] operator will raise an exception when the specified field does not exist.

Handling optional keys

By using the {} operator instead of [], it will return nil when the field is not found. The get-family of procedures will return a type’s default value when called on nil.

  1. import std/json
  2. let jsonNode = parseJson("{}")
  3. doAssert jsonNode{"nope"}.getInt() == 0
  4. doAssert jsonNode{"nope"}.getFloat() == 0
  5. doAssert jsonNode{"nope"}.getStr() == ""
  6. doAssert jsonNode{"nope"}.getBool() == false

Using default values

The get-family helpers also accept an additional parameter which allow you to fallback to a default value should the key’s values be null:

  1. import std/json
  2. let jsonNode = parseJson("""{"key": 3.14, "key2": null}""")
  3. doAssert jsonNode["key"].getFloat(6.28) == 3.14
  4. doAssert jsonNode["key2"].getFloat(3.14) == 3.14
  5. doAssert jsonNode{"nope"}.getFloat(3.14) == 3.14 # note the {}

Unmarshalling

In addition to reading dynamic data, Nim can also unmarshal JSON directly into a type with the to macro.

Note: Use Option for keys sometimes missing in json responses, and backticks around keys with a reserved keyword as name.

  1. import std/json
  2. import std/options
  3. type
  4. User = object
  5. name: string
  6. age: int
  7. `type`: Option[string]
  8. let userJson = parseJson("""{ "name": "Nim", "age": 12 }""")
  9. let user = to(userJson, User)
  10. if user.`type`.isSome():
  11. assert user.`type`.get() != "robot"

Creating JSON

This module can also be used to comfortably create JSON using the %* operator:

  1. import std/json
  2. var hisName = "John"
  3. let herAge = 31
  4. var j = %*
  5. [
  6. { "name": hisName, "age": 30 },
  7. { "name": "Susan", "age": herAge }
  8. ]
  9. var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
  10. j2["details"] = %* {"age":35, "pi":3.1415}
  11. echo j2

See also: std/jsonutils for hookable json serialization/deserialization of arbitrary types.

Example:

  1. import std/json
  2. ## Note: for JObject, key ordering is preserved, unlike in some languages,
  3. ## this is convenient for some use cases. Example:
  4. type Foo = object
  5. a1, a2, a0, a3, a4: int
  6. doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""

Imports

hashes, tables, strutils, lexbase, streams, macros, parsejson, options, since

Types

  1. JsonNode = ref JsonNodeObj

JSON node Source Edit

  1. JsonNodeKind = enum
  2. JNull, JBool, JInt, JFloat, JString, JObject, JArray

possible JSON node types Source Edit

  1. JsonNodeObj {.acyclic.} = object
  2. case kind*: JsonNodeKind
  3. of JString:
  4. str*: string
  5. of JInt:
  6. num*: BiggestInt
  7. of JFloat:
  8. fnum*: float
  9. of JBool:
  10. bval*: bool
  11. of JNull:
  12. nil
  13. of JObject:
  14. fields*: OrderedTable[string, JsonNode]
  15. of JArray:
  16. elems*: seq[JsonNode]

Source Edit

Procs

  1. proc `$`(node: JsonNode): string {....raises: [], tags: [], forbids: [].}

Converts node to its JSON Representation on one line. Source Edit

  1. proc `%`(b: bool): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JBool JsonNode. Source Edit

  1. proc `%`(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode {.
  2. ...raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JObject JsonNode Source Edit

  1. proc `%`(n: BiggestInt): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JInt JsonNode. Source Edit

  1. proc `%`(n: BiggestUInt): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JInt JsonNode. Source Edit

  1. proc `%`(n: float): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JFloat JsonNode.

Example:

  1. assert $(%[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2]) == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]"""
  2. assert (%NaN).kind == JString
  3. assert (%0.0).kind == JFloat

Source Edit

  1. proc `%`(n: int): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JInt JsonNode. Source Edit

  1. proc `%`(n: uint): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JInt JsonNode. Source Edit

  1. proc `%`(o: enum): JsonNode

Construct a JsonNode that represents the specified enum value as a string. Creates a new JString JsonNode. Source Edit

  1. proc `%`(o: ref object): JsonNode

Generic constructor for JSON data. Creates a new JObject JsonNode Source Edit

  1. proc `%`(s: string): JsonNode {....raises: [], tags: [], forbids: [].}

Generic constructor for JSON data. Creates a new JString JsonNode. Source Edit

  1. proc `%`[T: object](o: T): JsonNode

Construct JsonNode from tuples and objects. Source Edit

  1. proc `%`[T](elements: openArray[T]): JsonNode

Generic constructor for JSON data. Creates a new JArray JsonNode Source Edit

  1. proc `%`[T](opt: Option[T]): JsonNode

Generic constructor for JSON data. Creates a new JNull JsonNode if opt is empty, otherwise it delegates to the underlying value. Source Edit

  1. proc `%`[T](table: Table[string, T] | OrderedTable[string, T]): JsonNode

Generic constructor for JSON data. Creates a new JObject JsonNode. Source Edit

  1. proc `==`(a, b: JsonNode): bool {.noSideEffect, ...raises: [], tags: [RootEffect],
  2. forbids: [].}

Check two nodes for equality Source Edit

  1. proc `[]`(node: JsonNode; index: BackwardsIndex): JsonNode {.inline, ...raises: [],
  2. tags: [], forbids: [].}

Gets the node at array.len-i in an array through the ^ operator.

i.e. j[^i] is a shortcut for j[j.len-i].

Example:

  1. let
  2. j = parseJson("[1,2,3,4,5]")
  3. doAssert j[^1].getInt == 5
  4. doAssert j[^2].getInt == 4

Source Edit

  1. proc `[]`(node: JsonNode; index: int): JsonNode {.inline, ...raises: [], tags: [],
  2. forbids: [].}

Gets the node at index in an Array. Result is undefined if index is out of bounds, but as long as array bound checks are enabled it will result in an exception. Source Edit

  1. proc `[]`(node: JsonNode; name: string): JsonNode {.inline, ...raises: [KeyError],
  2. tags: [], forbids: [].}

Gets a field from a JObject, which must not be nil. If the value at name does not exist, raises KeyError. Source Edit

  1. proc `[]`[U, V](a: JsonNode; x: HSlice[U, V]): JsonNode

Slice operation for JArray.

Returns the inclusive range [a[x.a], a[x.b]]:

Example:

  1. import json
  2. let arr = %[0,1,2,3,4,5]
  3. doAssert arr[2..4] == %[2,3,4]
  4. doAssert arr[2..^2] == %[2,3,4]
  5. doAssert arr[^4..^2] == %[2,3,4]

Source Edit

  1. proc `[]=`(obj: JsonNode; key: string; val: JsonNode) {.inline, ...raises: [],
  2. tags: [], forbids: [].}

Sets a field from a JObject. Source Edit

  1. proc add(father, child: JsonNode) {....raises: [], tags: [], forbids: [].}

Adds child to a JArray node father. Source Edit

  1. proc add(obj: JsonNode; key: string; val: JsonNode) {....raises: [], tags: [],
  2. forbids: [].}

Sets a field from a JObject. Source Edit

  1. proc contains(node: JsonNode; key: string): bool {....raises: [], tags: [],
  2. forbids: [].}

Checks if key exists in node. Source Edit

  1. proc contains(node: JsonNode; val: JsonNode): bool {....raises: [],
  2. tags: [RootEffect], forbids: [].}

Checks if val exists in array node. Source Edit

  1. proc copy(p: JsonNode): JsonNode {....raises: [], tags: [], forbids: [].}

Performs a deep copy of p. Source Edit

  1. proc delete(obj: JsonNode; key: string) {....raises: [KeyError], tags: [],
  2. forbids: [].}

Deletes obj[key]. Source Edit

  1. proc escapeJson(s: string): string {....raises: [], tags: [], forbids: [].}

Converts a string s to its JSON representation with quotes. Source Edit

  1. proc escapeJson(s: string; result: var string) {....raises: [], tags: [],
  2. forbids: [].}

Converts a string s to its JSON representation with quotes. Appends to result. Source Edit

  1. proc escapeJsonUnquoted(s: string): string {....raises: [], tags: [], forbids: [].}

Converts a string s to its JSON representation without quotes. Source Edit

  1. proc escapeJsonUnquoted(s: string; result: var string) {....raises: [], tags: [],
  2. forbids: [].}

Converts a string s to its JSON representation without quotes. Appends to result. Source Edit

  1. proc getBiggestInt(n: JsonNode; default: BiggestInt = 0): BiggestInt {.
  2. ...raises: [], tags: [], forbids: [].}

Retrieves the BiggestInt value of a JInt JsonNode.

Returns default if n is not a JInt, or if n is nil.

Source Edit

  1. proc getBool(n: JsonNode; default: bool = false): bool {....raises: [], tags: [],
  2. forbids: [].}

Retrieves the bool value of a JBool JsonNode.

Returns default if n is not a JBool, or if n is nil.

Source Edit

  1. proc getElems(n: JsonNode; default: seq[JsonNode] = @[]): seq[JsonNode] {.
  2. ...raises: [], tags: [], forbids: [].}

Retrieves the array of a JArray JsonNode.

Returns default if n is not a JArray, or if n is nil.

Source Edit

  1. proc getFields(n: JsonNode; default = initOrderedTable(2)): OrderedTable[string,
  2. JsonNode] {....raises: [], tags: [], forbids: [].}

Retrieves the key, value pairs of a JObject JsonNode.

Returns default if n is not a JObject, or if n is nil.

Source Edit

  1. proc getFloat(n: JsonNode; default: float = 0.0): float {....raises: [], tags: [],
  2. forbids: [].}

Retrieves the float value of a JFloat JsonNode.

Returns default if n is not a JFloat or JInt, or if n is nil.

Source Edit

  1. proc getInt(n: JsonNode; default: int = 0): int {....raises: [], tags: [],
  2. forbids: [].}

Retrieves the int value of a JInt JsonNode.

Returns default if n is not a JInt, or if n is nil.

Source Edit

  1. proc getOrDefault(node: JsonNode; key: string): JsonNode {....raises: [], tags: [],
  2. forbids: [].}

Gets a field from a node. If node is nil or not an object or value at key does not exist, returns nil Source Edit

  1. proc getStr(n: JsonNode; default: string = ""): string {....raises: [], tags: [],
  2. forbids: [].}

Retrieves the string value of a JString JsonNode.

Returns default if n is not a JString, or if n is nil.

Source Edit

  1. proc hash(n: JsonNode): Hash {.noSideEffect, ...raises: [Exception],
  2. tags: [RootEffect], forbids: [].}

Compute the hash for a JSON node Source Edit

  1. proc hash(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect,
  2. ...raises: [Exception], tags: [RootEffect], forbids: [].}

Source Edit

  1. proc hasKey(node: JsonNode; key: string): bool {....raises: [], tags: [],
  2. forbids: [].}

Checks if key exists in node. Source Edit

  1. proc len(n: JsonNode): int {....raises: [], tags: [], forbids: [].}

If n is a JArray, it returns the number of elements. If n is a JObject, it returns the number of pairs. Else it returns 0. Source Edit

  1. proc newJArray(): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JArray JsonNode Source Edit

  1. proc newJBool(b: bool): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JBool JsonNode. Source Edit

  1. proc newJFloat(n: float): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JFloat JsonNode. Source Edit

  1. proc newJInt(n: BiggestInt): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JInt JsonNode. Source Edit

  1. proc newJNull(): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JNull JsonNode. Source Edit

  1. proc newJObject(): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JObject JsonNode Source Edit

  1. proc newJString(s: string): JsonNode {....raises: [], tags: [], forbids: [].}

Creates a new JString JsonNode. Source Edit

  1. proc parseFile(filename: string): JsonNode {.
  2. ...raises: [IOError, OSError, JsonParsingError, ValueError],
  3. tags: [ReadIOEffect, WriteIOEffect], forbids: [].}

Parses file into a JsonNode. If file contains extra data, it will raise JsonParsingError. Source Edit

  1. proc parseJson(buffer: string; rawIntegers = false; rawFloats = false): JsonNode {.
  2. ...raises: [IOError, OSError, JsonParsingError, ValueError],
  3. tags: [ReadIOEffect, WriteIOEffect], forbids: [].}

Parses JSON from buffer. If buffer contains extra data, it will raise JsonParsingError. If rawIntegers is true, integer literals will not be converted to a JInt field but kept as raw numbers via JString. If rawFloats is true, floating point literals will not be converted to a JFloat field but kept as raw numbers via JString. Source Edit

  1. proc parseJson(s: Stream; filename: string = ""; rawIntegers = false;
  2. rawFloats = false): JsonNode {.
  3. ...raises: [IOError, OSError, IOError, OSError, JsonParsingError, ValueError],
  4. tags: [ReadIOEffect, WriteIOEffect], forbids: [].}

Parses from a stream s into a JsonNode. filename is only needed for nice error messages. If s contains extra data, it will raise JsonParsingError. This closes the stream s after it’s done. If rawIntegers is true, integer literals will not be converted to a JInt field but kept as raw numbers via JString. If rawFloats is true, floating point literals will not be converted to a JFloat field but kept as raw numbers via JString. Source Edit

  1. proc pretty(node: JsonNode; indent = 2): string {....raises: [], tags: [],
  2. forbids: [].}

Returns a JSON Representation of node, with indentation and on multiple lines.

Similar to prettyprint in Python.

Example:

  1. let j = %* {"name": "Isaac", "books": ["Robot Dreams"],
  2. "details": {"age": 35, "pi": 3.1415}}
  3. doAssert pretty(j) == """
  4. {
  5. "name": "Isaac",
  6. "books": [
  7. "Robot Dreams"
  8. ],
  9. "details": {
  10. "age": 35,
  11. "pi": 3.1415
  12. }
  13. }"""

Source Edit

  1. proc to[T](node: JsonNode; t: typedesc[T]): T

Unmarshals the specified node into the object type specified.

Known limitations:

  • Heterogeneous arrays are not supported.
  • Sets in object variants are not supported.
  • Not nil annotations are not supported.

Example:

  1. let jsonNode = parseJson("""
  2. {
  3. "person": {
  4. "name": "Nimmer",
  5. "age": 21
  6. },
  7. "list": [1, 2, 3, 4]
  8. }
  9. """)
  10. type
  11. Person = object
  12. name: string
  13. age: int
  14. Data = object
  15. person: Person
  16. list: seq[int]
  17. var data = to(jsonNode, Data)
  18. doAssert data.person.name == "Nimmer"
  19. doAssert data.person.age == 21
  20. doAssert data.list == @[1, 2, 3, 4]

Source Edit

  1. proc toUgly(result: var string; node: JsonNode) {....raises: [], tags: [],
  2. forbids: [].}

Converts node to its JSON Representation, without regard for human readability. Meant to improve $ string conversion performance.

JSON representation is stored in the passed result

This provides higher efficiency than the pretty procedure as it does not attempt to format the resulting JSON to make it human readable.

Source Edit

  1. proc `{}`(node: JsonNode; index: varargs[int]): JsonNode {....raises: [], tags: [],
  2. forbids: [].}

Traverses the node and gets the given value. If any of the indexes do not exist, returns nil. Also returns nil if one of the intermediate data structures is not an array. Source Edit

  1. proc `{}`(node: JsonNode; key: string): JsonNode {....raises: [], tags: [],
  2. forbids: [].}

Gets a field from a node. If node is nil or not an object or value at key does not exist, returns nil Source Edit

  1. proc `{}`(node: JsonNode; keys: varargs[string]): JsonNode {....raises: [],
  2. tags: [], forbids: [].}

Traverses the node and gets the given value. If any of the keys do not exist, returns nil. Also returns nil if one of the intermediate data structures is not an object.

This proc can be used to create tree structures on the fly (sometimes called autovivification):

Example:

  1. var myjson = %* {"parent": {"child": {"grandchild": 1}}}
  2. doAssert myjson{"parent", "child", "grandchild"} == newJInt(1)

Source Edit

  1. proc `{}=`(node: JsonNode; keys: varargs[string]; value: JsonNode) {.
  2. ...raises: [KeyError], tags: [], forbids: [].}

Traverses the node and tries to set the value at the given location to value. If any of the keys are missing, they are added. Source Edit

Iterators

  1. iterator items(node: JsonNode): JsonNode {....raises: [], tags: [], forbids: [].}

Iterator for the items of node. node has to be a JArray. Source Edit

  1. iterator keys(node: JsonNode): string {....raises: [], tags: [], forbids: [].}

Iterator for the keys in node. node has to be a JObject. Source Edit

  1. iterator mitems(node: var JsonNode): var JsonNode {....raises: [], tags: [],
  2. forbids: [].}

Iterator for the items of node. node has to be a JArray. Items can be modified. Source Edit

  1. iterator mpairs(node: var JsonNode): tuple[key: string, val: var JsonNode] {.
  2. ...raises: [], tags: [], forbids: [].}

Iterator for the child elements of node. node has to be a JObject. Values can be modified Source Edit

  1. iterator pairs(node: JsonNode): tuple[key: string, val: JsonNode] {....raises: [],
  2. tags: [], forbids: [].}

Iterator for the child elements of node. node has to be a JObject. Source Edit

  1. iterator parseJsonFragments(s: Stream; filename: string = "";
  2. rawIntegers = false; rawFloats = false): JsonNode {.
  3. ...raises: [IOError, OSError, IOError, OSError, JsonParsingError, ValueError],
  4. tags: [ReadIOEffect, WriteIOEffect], forbids: [].}

Parses from a stream s into JsonNodes. filename is only needed for nice error messages. The JSON fragments are separated by whitespace. This can be substantially faster than the comparable loop for x in splitWhitespace(s): yield parseJson(x). This closes the stream s after it’s done. If rawIntegers is true, integer literals will not be converted to a JInt field but kept as raw numbers via JString. If rawFloats is true, floating point literals will not be converted to a JFloat field but kept as raw numbers via JString. Source Edit

Macros

  1. macro `%*`(x: untyped): untyped

Convert an expression to a JsonNode directly, without having to specify % for every element. Source Edit

  1. macro isRefSkipDistinct(arg: typed): untyped

internal only, do not use Source Edit

Templates

  1. template `%`(j: JsonNode): JsonNode

Source Edit

Exports

$, $, $, $, $, $, JsonEventKind, JsonError, JsonParser, JsonKindError, open, open, open, open, open, open, close, close, close, close, close, str, getInt, getFloat, kind, kind, getColumn, getLine, getFilename, errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr, nimIdentNormalize