json
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.
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:
import json let jsonNode = parseJson("""{"key": 3.14}""") doAssert jsonNode.kind == JObject 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:
import json let jsonNode = parseJson("""{"key": 3.14}""") 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
.
import json let jsonNode = parseJson("{}") doAssert jsonNode{"nope"}.getInt() == 0 doAssert jsonNode{"nope"}.getFloat() == 0 doAssert jsonNode{"nope"}.getStr() == "" 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
:
import json let jsonNode = parseJson("""{"key": 3.14, "key2": null}""") doAssert jsonNode["key"].getFloat(6.28) == 3.14 doAssert jsonNode["key2"].getFloat(3.14) == 3.14 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.
import json import options type User = object name: string age: int `type`: Option[string] let userJson = parseJson("""{ "name": "Nim", "age": 12 }""") let user = to(userJson, User) if user.`type`.isSome(): assert user.`type`.get() != "robot"
Creating JSON
This module can also be used to comfortably create JSON using the %*
operator:
import json var hisName = "John" let herAge = 31 var j = %* [ { "name": hisName, "age": 30 }, { "name": "Susan", "age": herAge } ] var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]} j2["details"] = %* {"age":35, "pi":3.1415} echo j2
See also: std/jsonutils for hookable json serialization/deserialization of arbitrary types.
Example:
## Note: for JObject, key ordering is preserved, unlike in some languages, ## this is convenient for some use cases. Example: type Foo = object a1, a2, a0, a3, a4: int doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""
Imports
Types
JsonNodeKind = enum JNull, JBool, JInt, JFloat, JString, JObject, JArray
- possible JSON node types Source Edit
JsonNode = ref JsonNodeObj
- JSON node Source Edit
JsonNodeObj {...}{.acyclic.} = object isUnquoted: bool case kind*: JsonNodeKind of JString: str*: string of JInt: num*: BiggestInt of JFloat: fnum*: float of JBool: bval*: bool of JNull: nil of JObject: fields*: OrderedTable[string, JsonNode] of JArray: elems*: seq[JsonNode]
- Source Edit
Procs
proc newJString(s: string): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JString JsonNode
. Source Edit proc newJInt(n: BiggestInt): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JInt JsonNode
. Source Edit proc newJFloat(n: float): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JFloat JsonNode
. Source Edit proc newJBool(b: bool): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JBool JsonNode
. Source Edit proc newJNull(): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JNull JsonNode
. Source Edit proc newJObject(): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JObject JsonNode
Source Edit proc newJArray(): JsonNode {...}{.raises: [], tags: [].}
- Creates a new
JArray JsonNode
Source Edit proc getStr(n: JsonNode; default: string = ""): string {...}{.raises: [], tags: [].}
-
Retrieves the string value of a
JString JsonNode
.Returns
Source Editdefault
ifn
is not aJString
, or ifn
is nil. proc getInt(n: JsonNode; default: int = 0): int {...}{.raises: [], tags: [].}
-
Retrieves the int value of a
JInt JsonNode
.Returns
Source Editdefault
ifn
is not aJInt
, or ifn
is nil. proc getBiggestInt(n: JsonNode; default: BiggestInt = 0): BiggestInt {...}{. raises: [], tags: [].}
-
Retrieves the BiggestInt value of a
JInt JsonNode
.Returns
Source Editdefault
ifn
is not aJInt
, or ifn
is nil. proc getFloat(n: JsonNode; default: float = 0.0): float {...}{.raises: [], tags: [].}
-
Retrieves the float value of a
JFloat JsonNode
.Returns
Source Editdefault
ifn
is not aJFloat
orJInt
, or ifn
is nil. proc getBool(n: JsonNode; default: bool = false): bool {...}{.raises: [], tags: [].}
-
Retrieves the bool value of a
JBool JsonNode
.Returns
Source Editdefault
ifn
is not aJBool
, or ifn
is nil. proc getFields(n: JsonNode; default = initOrderedTable(2)): OrderedTable[string, JsonNode] {...}{.raises: [], tags: [].}
-
Retrieves the key, value pairs of a
JObject JsonNode
.Returns
Source Editdefault
ifn
is not aJObject
, or ifn
is nil. proc getElems(n: JsonNode; default: seq[JsonNode] = @[]): seq[JsonNode] {...}{. raises: [], tags: [].}
-
Retrieves the array of a
JArray JsonNode
.Returns
Source Editdefault
ifn
is not aJArray
, or ifn
is nil. proc add(father, child: JsonNode) {...}{.raises: [], tags: [].}
- Adds
child
to a JArray nodefather
. Source Edit proc add(obj: JsonNode; key: string; val: JsonNode) {...}{.raises: [], tags: [].}
- Sets a field from a
JObject
. Source Edit proc `%`(s: string): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JString JsonNode
. Source Edit proc `%`(n: uint): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JInt JsonNode
. Source Edit proc `%`(n: int): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JInt JsonNode
. Source Edit proc `%`(n: BiggestUInt): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JInt JsonNode
. Source Edit proc `%`(n: BiggestInt): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JInt JsonNode
. Source Edit proc `%`(n: float): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JFloat JsonNode
. Source Edit proc `%`(b: bool): JsonNode {...}{.raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JBool JsonNode
. Source Edit proc `%`(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode {...}{. raises: [], tags: [].}
- Generic constructor for JSON data. Creates a new
JObject JsonNode
Source Edit proc `%`[T](elements: openArray[T]): JsonNode
- Generic constructor for JSON data. Creates a new
JArray JsonNode
Source Edit proc `%`[T](table: Table[string, T] | OrderedTable[string, T]): JsonNode
- Generic constructor for JSON data. Creates a new
JObject JsonNode
. Source Edit proc `%`[T](opt: Option[T]): JsonNode
- Generic constructor for JSON data. Creates a new
JNull JsonNode
ifopt
is empty, otherwise it delegates to the underlying value. Source Edit proc `[]=`(obj: JsonNode; key: string; val: JsonNode) {...}{.inline, raises: [], tags: [].}
- Sets a field from a
JObject
. Source Edit proc `%`[T: object](o: T): JsonNode
- Construct JsonNode from tuples and objects. Source Edit
proc `%`(o: ref object): JsonNode
- Generic constructor for JSON data. Creates a new
JObject JsonNode
Source Edit proc `%`(o: enum): JsonNode
- Construct a JsonNode that represents the specified enum value as a string. Creates a new
JString JsonNode
. Source Edit proc `==`(a, b: JsonNode): bool {...}{.raises: [KeyError], tags: [].}
- Check two nodes for equality Source Edit
proc hash(n: JsonNode): Hash {...}{.raises: [Exception], tags: [RootEffect].}
- Compute the hash for a JSON node Source Edit
proc hash(n: OrderedTable[string, JsonNode]): Hash {...}{.noSideEffect, raises: [Exception], tags: [RootEffect].}
- Source Edit
proc len(n: JsonNode): int {...}{.raises: [], tags: [].}
- If
n
is aJArray
, it returns the number of elements. Ifn
is aJObject
, it returns the number of pairs. Else it returns 0. Source Edit proc `[]`(node: JsonNode; name: string): JsonNode {...}{.inline, raises: [KeyError], tags: [].}
- Gets a field from a
JObject
, which must not be nil. If the value atname
does not exist, raises KeyError. Source Edit proc `[]`(node: JsonNode; index: int): JsonNode {...}{.inline, raises: [], tags: [].}
- Gets the node at
index
in an Array. Result is undefined ifindex
is out of bounds, but as long as array bound checks are enabled it will result in an exception. Source Edit proc hasKey(node: JsonNode; key: string): bool {...}{.raises: [], tags: [].}
- Checks if
key
exists innode
. Source Edit proc contains(node: JsonNode; key: string): bool {...}{.raises: [], tags: [].}
- Checks if
key
exists innode
. Source Edit proc contains(node: JsonNode; val: JsonNode): bool {...}{.raises: [KeyError], tags: [].}
- Checks if
val
exists in arraynode
. Source Edit proc `{}`(node: JsonNode; keys: varargs[string]): JsonNode {...}{.raises: [], tags: [].}
-
Traverses the node and gets the given value. If any of the keys do not exist, returns
nil
. Also returnsnil
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:
var myjson = %* {"parent": {"child": {"grandchild": 1}}} doAssert myjson{"parent", "child", "grandchild"} == newJInt(1)
Source Edit proc `{}`(node: JsonNode; index: varargs[int]): JsonNode {...}{.raises: [], tags: [].}
- Traverses the node and gets the given value. If any of the indexes do not exist, returns
nil
. Also returnsnil
if one of the intermediate data structures is not an array. Source Edit proc getOrDefault(node: JsonNode; key: string): JsonNode {...}{.raises: [], tags: [].}
- Gets a field from a
node
. Ifnode
is nil or not an object or value atkey
does not exist, returns nil Source Edit proc `{}`(node: JsonNode; key: string): JsonNode {...}{.raises: [], tags: [].}
- Gets a field from a
node
. Ifnode
is nil or not an object or value atkey
does not exist, returns nil Source Edit proc `{}=`(node: JsonNode; keys: varargs[string]; value: JsonNode) {...}{. raises: [KeyError], tags: [].}
- 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 proc delete(obj: JsonNode; key: string) {...}{.raises: [KeyError], tags: [].}
- Deletes
obj[key]
. Source Edit proc copy(p: JsonNode): JsonNode {...}{.raises: [], tags: [].}
- Performs a deep copy of
a
. Source Edit proc escapeJsonUnquoted(s: string; result: var string) {...}{.raises: [], tags: [].}
- Converts a string
s
to its JSON representation without quotes. Appends toresult
. Source Edit proc escapeJsonUnquoted(s: string): string {...}{.raises: [], tags: [].}
- Converts a string
s
to its JSON representation without quotes. Source Edit proc escapeJson(s: string; result: var string) {...}{.raises: [], tags: [].}
- Converts a string
s
to its JSON representation with quotes. Appends toresult
. Source Edit proc escapeJson(s: string): string {...}{.raises: [], tags: [].}
- Converts a string
s
to its JSON representation with quotes. Source Edit proc pretty(node: JsonNode; indent = 2): string {...}{.raises: [], tags: [].}
-
Returns a JSON Representation of
node
, with indentation and on multiple lines.Similar to prettyprint in Python.
Example:
let j = %* {"name": "Isaac", "books": ["Robot Dreams"], "details": {"age": 35, "pi": 3.1415}} doAssert pretty(j) == """ { "name": "Isaac", "books": [ "Robot Dreams" ], "details": { "age": 35, "pi": 3.1415 } }"""
Source Edit proc toUgly(result: var string; node: JsonNode) {...}{.raises: [], tags: [].}
-
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
Source Editpretty
procedure as it does not attempt to format the resulting JSON to make it human readable. proc `$`(node: JsonNode): string {...}{.raises: [], tags: [].}
- Converts
node
to its JSON Representation on one line. Source Edit proc parseJson(s: Stream; filename: string = ""; rawIntegers = false; rawFloats = false): JsonNode {...}{.raises: [IOError, OSError, IOError, OSError, JsonParsingError, ValueError, Exception], tags: [ReadIOEffect, WriteIOEffect].}
- Parses from a stream
s
into aJsonNode
.filename
is only needed for nice error messages. Ifs
contains extra data, it will raiseJsonParsingError
. This closes the streams
after it's done. IfrawIntegers
is true, integer literals will not be converted to aJInt
field but kept as raw numbers viaJString
. IfrawFloats
is true, floating point literals will not be converted to aJFloat
field but kept as raw numbers viaJString
. Source Edit proc parseJson(buffer: string; rawIntegers = false; rawFloats = false): JsonNode {...}{. raises: [IOError, OSError, JsonParsingError, ValueError, Exception], tags: [ReadIOEffect, WriteIOEffect].}
- Parses JSON from
buffer
. Ifbuffer
contains extra data, it will raiseJsonParsingError
. IfrawIntegers
is true, integer literals will not be converted to aJInt
field but kept as raw numbers viaJString
. IfrawFloats
is true, floating point literals will not be converted to aJFloat
field but kept as raw numbers viaJString
. Source Edit proc parseFile(filename: string): JsonNode {...}{. raises: [IOError, OSError, JsonParsingError, ValueError, Exception], tags: [ReadIOEffect, WriteIOEffect].}
- Parses
file
into aJsonNode
. Iffile
contains extra data, it will raiseJsonParsingError
. Source Edit 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:
let jsonNode = parseJson(""" { "person": { "name": "Nimmer", "age": 21 }, "list": [1, 2, 3, 4] } """) type Person = object name: string age: int Data = object person: Person list: seq[int] var data = to(jsonNode, Data) doAssert data.person.name == "Nimmer" doAssert data.person.age == 21 doAssert data.list == @[1, 2, 3, 4]
Source Edit
Iterators
iterator items(node: JsonNode): JsonNode {...}{.raises: [], tags: [].}
- Iterator for the items of
node
.node
has to be a JArray. Source Edit iterator mitems(node: var JsonNode): var JsonNode {...}{.raises: [], tags: [].}
- Iterator for the items of
node
.node
has to be a JArray. Items can be modified. Source Edit iterator pairs(node: JsonNode): tuple[key: string, val: JsonNode] {...}{.raises: [], tags: [].}
- Iterator for the child elements of
node
.node
has to be a JObject. Source Edit iterator keys(node: JsonNode): string {...}{.raises: [], tags: [].}
- Iterator for the keys in
node
.node
has to be a JObject. Source Edit iterator mpairs(node: var JsonNode): tuple[key: string, val: var JsonNode] {...}{. raises: [], tags: [].}
- Iterator for the child elements of
node
.node
has to be a JObject. Values can be modified Source Edit iterator parseJsonFragments(s: Stream; filename: string = ""; rawIntegers = false; rawFloats = false): JsonNode {...}{.raises: [ IOError, OSError, IOError, OSError, JsonParsingError, ValueError, Exception], tags: [ReadIOEffect, WriteIOEffect].}
- Parses from a stream
s
intoJsonNodes
.filename
is only needed for nice error messages. The JSON fragments are separated by whitespace. This can be substantially faster than the comparable loopfor x in splitWhitespace(s): yield parseJson(x)
. This closes the streams
after it's done. IfrawIntegers
is true, integer literals will not be converted to aJInt
field but kept as raw numbers viaJString
. IfrawFloats
is true, floating point literals will not be converted to aJFloat
field but kept as raw numbers viaJString
. Source Edit
Macros
macro `%*`(x: untyped): untyped
- Convert an expression to a JsonNode directly, without having to specify
%
for every element. Source Edit macro isRefSkipDistinct(arg: typed): untyped
- internal only, do not use Source Edit
Templates
Exports
- $, $, $, $, $, $, JsonEventKind, JsonError, JsonParser, JsonKindError, open, open, open, open, open, close, close, close, close, str, getInt, getFloat, kind, kind, getColumn, getLine, getFilename, errorMsg, errorMsgExpected, next, JsonParsingError, raiseParseErr, nimIdentNormalize
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/json.html