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.
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
.
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.
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
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
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
JsonParser = object of BaseLexer a: string tok: TokKind kind: JsonEventKind err: JsonError state: seq[ParserState] filename: string
JsonKindError = object of ValueError
to
macro if the JSON kind is incorrect. JsonNodeKind = enum JNull, JBool, JInt, JFloat, JString, JObject, JArray
JsonNode = ref JsonNodeObj
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
proc open(my: var JsonParser; input: Stream; filename: string) {.raises: [Exception], tags: [ReadIOEffect].}
proc close(my: var JsonParser) {.inline, raises: [Exception], tags: [].}
proc str(my: JsonParser): string {.inline, raises: [], tags: [].}
jsonInt
, jsonFloat
, jsonString
proc getInt(my: JsonParser): BiggestInt {.inline, raises: [ValueError], tags: [].}
jsonInt
proc getFloat(my: JsonParser): float {.inline, raises: [ValueError], tags: [].}
jsonFloat
proc kind(my: JsonParser): JsonEventKind {.inline, raises: [], tags: [].}
proc getColumn(my: JsonParser): int {.inline, raises: [], tags: [].}
proc getLine(my: JsonParser): int {.inline, raises: [], tags: [].}
proc getFilename(my: JsonParser): string {.inline, raises: [], tags: [].}
proc errorMsg(my: JsonParser): string {.raises: [ValueError], tags: [].}
jsonError
proc errorMsgExpected(my: JsonParser; e: string): string {.raises: [ValueError], tags: [].}
proc next(my: var JsonParser) {.raises: [Exception], tags: [ReadIOEffect].}
proc raiseParseErr(p: JsonParser; msg: string) {.noinline, noreturn, raises: [JsonParsingError, ValueError], tags: [].}
proc newJString(s: string): JsonNode {.raises: [], tags: [].}
proc newJInt(n: BiggestInt): JsonNode {.raises: [], tags: [].}
proc newJFloat(n: float): JsonNode {.raises: [], tags: [].}
proc newJBool(b: bool): JsonNode {.raises: [], tags: [].}
proc newJNull(): JsonNode {.raises: [], tags: [].}
proc newJObject(): JsonNode {.raises: [], tags: [].}
proc newJArray(): JsonNode {.raises: [], tags: [].}
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: [].}
proc `%`(n: BiggestInt): JsonNode {.raises: [], tags: [].}
proc `%`(n: float): JsonNode {.raises: [], tags: [].}
proc `%`(b: bool): JsonNode {.raises: [], tags: [].}
proc `%`(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode {.raises: [], tags: [].}
proc `%`[T](elements: openArray[T]): JsonNode
proc `%`(o: object): JsonNode
proc `%`(o: ref object): JsonNode
proc `%`(o: enum): JsonNode
JString JsonNode
. proc `==`(a, b: JsonNode): bool {.raises: [KeyError], tags: [].}
proc hash(n: JsonNode): Hash {.raises: [Exception], tags: [RootEffect].}
proc hash(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect, raises: [Exception], tags: [RootEffect].}
proc len(n: JsonNode): int {.raises: [], tags: [].}
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: [].}
proc hasKey(node: JsonNode; key: string): bool {.raises: [], tags: [].}
proc contains(node: JsonNode; key: string): bool {.raises: [], tags: [].}
proc contains(node: JsonNode; val: JsonNode): bool {.raises: [KeyError], tags: [].}
proc existsKey(node: JsonNode; key: string): bool {.deprecated, raises: [], tags: [].}
proc add(father, child: JsonNode) {.raises: [], tags: [].}
proc add(obj: JsonNode; key: string; val: JsonNode) {.raises: [], tags: [].}
proc `[]=`(obj: JsonNode; key: string; val: JsonNode) {.inline, raises: [], tags: [].}
proc `{}`(node: JsonNode; keys: varargs[string]): JsonNode {.raises: [], tags: [].}
nil
. Also returns nil
if one of the intermediate data structures is not an object. proc getOrDefault(node: JsonNode; key: string): JsonNode {.raises: [], tags: [].}
proc `{}=`(node: JsonNode; keys: varargs[string]; value: JsonNode) {. raises: [KeyError], tags: [].}
value
. If any of the keys are missing, they are added. proc delete(obj: JsonNode; key: string) {.raises: [IndexError], tags: [].}
obj[key]
. proc copy(p: JsonNode): JsonNode {.raises: [], tags: [].}
proc escapeJson(s: string; result: var string) {.raises: [], tags: [].}
result
. proc escapeJson(s: string): string {.raises: [], tags: [].}
proc pretty(node: JsonNode; indent = 2): string {.raises: [], tags: [].}
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: [].}
proc parseJson(s: Stream; filename: string): JsonNode {. raises: [Exception, Exception, ValueError, JsonParsingError], tags: [ReadIOEffect].}
proc parseJson(buffer: string): JsonNode {.raises: [Exception, ValueError, JsonParsingError], tags: [ReadIOEffect].}
proc parseFile(filename: string): JsonNode {. raises: [IOError, Exception, ValueError, JsonParsingError], tags: [ReadIOEffect].}
iterator items(node: JsonNode): JsonNode {.raises: [], tags: [].}
iterator mitems(node: var JsonNode): var JsonNode {.raises: [], tags: [].}
iterator pairs(node: JsonNode): tuple[key: string, val: JsonNode] {.raises: [], tags: [].}
iterator mpairs(node: var JsonNode): tuple[key: string, val: var JsonNode] {.raises: [], tags: [].}
macro `%*`(x: untyped): untyped
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]
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