W3cubDocs

/Nim

Module 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.

Dynamically retrieving fields from JSON

This module allows you to access fields in a parsed JSON object in two different ways, one of them is described in this section.

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 acess its fields using the [] operator. The following example shows how to do this:

let jsonNode = parseJson("""{"key": 3.14}""")
doAssert jsonNode.kind == JObject
doAssert jsonNode["key"].kind == JFloat

Retrieving the value of a JSON node can then be achieved using one of the helper procedures, which include:

  • getNum
  • getFNum
  • getStr
  • getBVal

To retrieve the value of "key" you can do the following:

doAssert jsonNode["key"].getFNum() == 3.14

The [] operator will raise an exception when the specified field does not exist. If you wish to avoid this behaviour you can use the {} operator instead, it will simply return nil when the field is not found. The get-family of procedures will return a default value when called on nil.

Unmarshalling JSON into a type

This module allows you to access fields in a parsed JSON object in two different ways, one of them is described in this section.

This is done using the to macro. Take a look at its documentation to see an example of its use.

Creating JSON

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

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

Imports

hashes, tables, strutils, lexbase, streams, unicode, macros

Types

JsonEventKind = enum
  jsonError,                  ## an error occurred during parsing
  jsonEof,                    ## end of file reached
  jsonString,                 ## a string literal
  jsonInt,                    ## an integer literal
  jsonFloat,                  ## a float literal
  jsonTrue,                   ## the value ``true``
  jsonFalse,                  ## the value ``false``
  jsonNull,                   ## the value ``null``
  jsonObjectStart,            ## start of an object: the ``{`` token
  jsonObjectEnd,              ## end of an object: the ``}`` token
  jsonArrayStart,             ## start of an array: the ``[`` token
  jsonArrayEnd                ## start of an array: the ``]`` token
enumeration of all events that may occur when parsing
JsonError = enum
  errNone,                    ## no error
  errInvalidToken,            ## invalid token
  errStringExpected,          ## string expected
  errColonExpected,           ## ``:`` expected
  errCommaExpected,           ## ``,`` expected
  errBracketRiExpected,       ## ``]`` expected
  errCurlyRiExpected,         ## ``}`` expected
  errQuoteExpected,           ## ``"`` or ``'`` expected
  errEOC_Expected,            ## ``*/`` expected
  errEofExpected,             ## EOF expected
  errExprExpected             ## expr expected
enumeration that lists all errors that can occur
JsonParser = object of BaseLexer
  a: string
  tok: TokKind
  kind: JsonEventKind
  err: JsonError
  state: seq[ParserState]
  filename: string
the parser object.
JsonKindError = object of ValueError
raised by the to macro if the JSON kind is incorrect.
JsonNodeKind = enum
  JNull, JBool, JInt, JFloat, JString, JObject, JArray
possible JSON node types
JsonNode = ref JsonNodeObj
JSON node
JsonNodeObj = object
  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]
JsonParsingError = object of ValueError
is raised for a JSON error

Procs

proc open(my: var JsonParser; input: Stream; filename: string) {.raises: [Exception],
    tags: [ReadIOEffect].}
initializes the parser with an input stream. Filename is only used for nice error messages.
proc close(my: var JsonParser) {.inline, raises: [Exception], tags: [].}
closes the parser my and its associated input stream.
proc str(my: JsonParser): string {.inline, raises: [], tags: [].}
returns the character data for the events: jsonInt, jsonFloat, jsonString
proc getInt(my: JsonParser): BiggestInt {.inline, raises: [ValueError], tags: [].}
returns the number for the event: jsonInt
proc getFloat(my: JsonParser): float {.inline, raises: [ValueError], tags: [].}
returns the number for the event: jsonFloat
proc kind(my: JsonParser): JsonEventKind {.inline, raises: [], tags: [].}
returns the current event type for the JSON parser
proc getColumn(my: JsonParser): int {.inline, raises: [], tags: [].}
get the current column the parser has arrived at.
proc getLine(my: JsonParser): int {.inline, raises: [], tags: [].}
get the current line the parser has arrived at.
proc getFilename(my: JsonParser): string {.inline, raises: [], tags: [].}
get the filename of the file that the parser processes.
proc errorMsg(my: JsonParser): string {.raises: [ValueError], tags: [].}
returns a helpful error message for the event jsonError
proc errorMsgExpected(my: JsonParser; e: string): string {.raises: [ValueError],
    tags: [].}
returns an error message "e expected" in the same format as the other error messages
proc next(my: var JsonParser) {.raises: [Exception], tags: [ReadIOEffect].}
retrieves the first/next event. This controls the parser.
proc raiseParseErr(p: JsonParser; msg: string) {.noinline, noreturn,
    raises: [JsonParsingError, ValueError], tags: [].}
raises an EJsonParsingError exception.
proc newJString(s: string): JsonNode {.raises: [], tags: [].}
Creates a new JString JsonNode.
proc newJInt(n: BiggestInt): JsonNode {.raises: [], tags: [].}
Creates a new JInt JsonNode.
proc newJFloat(n: float): JsonNode {.raises: [], tags: [].}
Creates a new JFloat JsonNode.
proc newJBool(b: bool): JsonNode {.raises: [], tags: [].}
Creates a new JBool JsonNode.
proc newJNull(): JsonNode {.raises: [], tags: [].}
Creates a new JNull JsonNode.
proc newJObject(): JsonNode {.raises: [], tags: [].}
Creates a new JObject JsonNode
proc newJArray(): JsonNode {.raises: [], tags: [].}
Creates a new JArray JsonNode
proc getStr(n: JsonNode; default: string = ""): string {.raises: [], tags: [].}

Retrieves the string value of a JString JsonNode.

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

proc getNum(n: JsonNode; default: BiggestInt = 0): BiggestInt {.raises: [], tags: [].}

Retrieves the int value of a JInt JsonNode.

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

proc getFNum(n: JsonNode; default: float = 0.0): float {.raises: [], tags: [].}

Retrieves the float value of a JFloat JsonNode.

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

proc getBVal(n: JsonNode; default: bool = false): bool {.raises: [], tags: [].}

Retrieves the bool value of a JBool JsonNode.

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

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

Retrieves the key, value pairs of a JObject JsonNode.

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

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

Retrieves the int value of a JArray JsonNode.

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

proc `%`(s: string): JsonNode {.raises: [], tags: [].}
Generic constructor for JSON data. Creates a new JString JsonNode.
proc `%`(n: BiggestInt): JsonNode {.raises: [], tags: [].}
Generic constructor for JSON data. Creates a new JInt JsonNode.
proc `%`(n: float): JsonNode {.raises: [], tags: [].}
Generic constructor for JSON data. Creates a new JFloat JsonNode.
proc `%`(b: bool): JsonNode {.raises: [], tags: [].}
Generic constructor for JSON data. Creates a new JBool JsonNode.
proc `%`(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode {.raises: [],
    tags: [].}
Generic constructor for JSON data. Creates a new JObject JsonNode
proc `%`[T](elements: openArray[T]): JsonNode
Generic constructor for JSON data. Creates a new JArray JsonNode
proc `%`(o: object): JsonNode
Generic constructor for JSON data. Creates a new JObject JsonNode
proc `%`(o: ref object): JsonNode
Generic constructor for JSON data. Creates a new JObject JsonNode
proc `%`(o: enum): JsonNode
Construct a JsonNode that represents the specified enum value as a string. Creates a new JString JsonNode.
proc `==`(a, b: JsonNode): bool {.raises: [KeyError], tags: [].}
Check two nodes for equality
proc hash(n: JsonNode): Hash {.raises: [Exception], tags: [RootEffect].}
Compute the hash for a JSON node
proc hash(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect,
    raises: [Exception], tags: [RootEffect].}
proc len(n: JsonNode): int {.raises: [], tags: [].}
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.
proc `[]`(node: JsonNode; name: string): JsonNode {.inline, raises: [KeyError], tags: [].}

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

Note: The behaviour of this procedure changed in version 0.14.0. To get a list of usages and to restore the old behaviour of this procedure, compile with the -d:nimJsonGet flag.

proc `[]`(node: JsonNode; index: int): JsonNode {.inline, raises: [], tags: [].}
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.
proc hasKey(node: JsonNode; key: string): bool {.raises: [], tags: [].}
Checks if key exists in node.
proc contains(node: JsonNode; key: string): bool {.raises: [], tags: [].}
Checks if key exists in node.
proc contains(node: JsonNode; val: JsonNode): bool {.raises: [KeyError], tags: [].}
Checks if val exists in array node.
proc existsKey(node: JsonNode; key: string): bool {.deprecated, raises: [], tags: [].}
Deprecated for hasKey
proc add(father, child: JsonNode) {.raises: [], tags: [].}
Adds child to a JArray node father.
proc add(obj: JsonNode; key: string; val: JsonNode) {.raises: [], tags: [].}
Sets a field from a JObject.
proc `[]=`(obj: JsonNode; key: string; val: JsonNode) {.inline, raises: [], tags: [].}
Sets a field from a JObject.
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 returns nil if one of the intermediate data structures is not an object.
proc getOrDefault(node: JsonNode; key: string): JsonNode {.raises: [], tags: [].}
Gets a field from a node. If node is nil or not an object or value at key does not exist, returns nil
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.
proc delete(obj: JsonNode; key: string) {.raises: [IndexError], tags: [].}
Deletes obj[key].
proc copy(p: JsonNode): JsonNode {.raises: [], tags: [].}
Performs a deep copy of a.
proc escapeJson(s: string; result: var string) {.raises: [], tags: [].}
Converts a string s to its JSON representation. Appends to result.
proc escapeJson(s: string): string {.raises: [], tags: [].}
Converts a string s to its JSON representation.
proc pretty(node: JsonNode; indent = 2): string {.raises: [], tags: [].}
Returns a JSON Representation of node, with indentation and on multiple lines.
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 pretty 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.
proc parseJson(s: Stream; filename: string): JsonNode {.
    raises: [Exception, Exception, ValueError, JsonParsingError],
    tags: [ReadIOEffect].}
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.
proc parseJson(buffer: string): JsonNode {.raises: [Exception, ValueError,
    JsonParsingError], tags: [ReadIOEffect].}
Parses JSON from buffer. If buffer contains extra data, it will raise JsonParsingError.
proc parseFile(filename: string): JsonNode {.
    raises: [IOError, Exception, ValueError, JsonParsingError], tags: [ReadIOEffect].}
Parses file into a JsonNode. If file contains extra data, it will raise JsonParsingError.

Iterators

iterator items(node: JsonNode): JsonNode {.raises: [], tags: [].}
Iterator for the items of node. node has to be a JArray.
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.
iterator pairs(node: JsonNode): tuple[key: string, val: JsonNode] {.raises: [], tags: [].}
Iterator for the child elements of node. node has to be a JObject.
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

Macros

macro `%*`(x: untyped): untyped
Convert an expression to a JsonNode directly, without having to specify % for every element.
macro to(node: JsonNode; T: typedesc): untyped

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]

Templates

template `%`(j: JsonNode): JsonNode
template simpleGetOrDefault{{}(node, [key])
}(node: JsonNode; key: string): JsonNode

© 2006–2017 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/json.html