Spec-Zone.ru › Nim 1

json

Этот модуль реализует простой высокопроизводительный парсер JSON. JSON (JavaScript Object Notation) — это лёгкий формат обмена данными, удобный для чтения и записи человеком (в отличие от XML). Он также легко парсируется и генерируется машинами. JSON основан на подмножестве языка программирования JavaScript, Стандарт ECMA-262 3-го издания — декабрь 1999 года.

Обзор

Парсинг JSON

JSON часто поступает в вашу программу (через API или файл) в виде string. Первый шаг — преобразовать его из сериализованной формы в вложенную структуру объектов, называемую JsonNode.

Процедура parseJson принимает строку, содержащую JSON, и возвращает объект JsonNode. Это вариант объекта, который может быть JObject, JArray, JString, JInt, JFloat, JBool или JNull. Вы проверяете тип этого варианта объекта, используя аксессор kind.

Для JsonNode, тип которого JObject, вы можете получить доступ к его полям, используя оператор [] . Следующий пример демонстрирует это:

import json

let jsonNode = parseJson("""{"key": 3.14}""")

doAssert jsonNode.kind == JObject
doAssert jsonNode["key"].kind == JFloat

Чтение значений

После получения JsonNode, извлечение значений можно осуществить, используя одну из вспомогательных процедур, которые включают:

  • getInt
  • getFloat
  • getStr
  • getBool

Для получения значения "key" можно выполнить следующие действия:

import json

let jsonNode = parseJson("""{"key": 3.14}""")

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

Важно: оператор [] вызовет исключение, если указанное поле не существует.

Обработка необязательных ключей

Используя оператор {} вместо [], он вернёт nil при отсутствии поля. Процедуры из семейства get вернут значение по умолчанию типа, если они вызваны для 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

Использование значений по умолчанию

Вспомогательные процедуры из семейства get также принимают дополнительный параметр, позволяющий использовать значение по умолчанию, если значение ключа 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 {}

Распаковка

Помимо чтения динамических данных, Nim также может распаковать JSON непосредственно в тип с помощью макроса to.

Примечание: Используйте Option для ключей, которые иногда отсутствуют в ответах JSON, и обрамляйте ключи с ключевыми словами зарезервированными словами в обратные кавычки.

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"

Создание JSON

Этот модуль также можно использовать для удобного создания JSON с помощью оператора %*:

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

См. также: std/jsonutils для привяземых сериализации/десериализации произвольных типов JSON.

Пример:

## 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}"""

Импорты

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

Типы

JsonNodeKind = enum
  JNull, JBool, JInt, JFloat, JString, JObject, JArray
возможные типы узлов JSON Исходный код Редактировать
JsonNode = ref JsonNodeObj
узел JSON Исходный код Редактировать
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]
Исходный код Редактировать

Процедуры

proc newJString(s: string): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JString JsonNode. Исходный код Редактировать
proc newJInt(n: BiggestInt): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JInt JsonNode. Исходный код Редактировать
proc newJFloat(n: float): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JFloat JsonNode. Исходный код Редактировать
proc newJBool(b: bool): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JBool JsonNode. Исходный код Редактировать
proc newJNull(): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JNull JsonNode. Исходный код Редактировать
proc newJObject(): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JObject JsonNode Исходный код Редактировать
proc newJArray(): JsonNode {...}{.raises: [], tags: [].}
Создаёт новый JArray JsonNode Исходный код Редактировать
proc getStr(n: JsonNode; default: string = ""): string {...}{.raises: [], tags: [].}

Возвращает строковое значение JString JsonNode.

Возвращает default если n не является JString, или если n равно null.

Исходный код Редактировать
proc getInt(n: JsonNode; default: int = 0): int {...}{.raises: [], tags: [].}

Возвращает целочисленное значение JInt JsonNode.

Возвращает default если n не является JInt, или если n равно null.

Исходный код Редактировать
proc getBiggestInt(n: JsonNode; default: BiggestInt = 0): BiggestInt {...}{.
    raises: [], tags: [].}

Возвращает значение BiggestInt JInt JsonNode.

Возвращает default если n не является JInt, или если n равно null.

Исходный код Редактировать
proc getFloat(n: JsonNode; default: float = 0.0): float {...}{.raises: [], tags: [].}

Возвращает значение с плавающей точкой JFloat JsonNode.

Возвращает default если n не является JFloat или JInt, или если n равно null.

Исходный код Редактировать
proc getBool(n: JsonNode; default: bool = false): bool {...}{.raises: [], tags: [].}

Возвращает логическое значение JBool JsonNode.

Возвращает default если n не является JBool, или если n равно null.

Исходный код Редактировать
proc getFields(n: JsonNode; default = initOrderedTable(2)): OrderedTable[string,
    JsonNode] {...}{.raises: [], tags: [].}

Возвращает пары ключ-значение JObject JsonNode.

Возвращает default если n не является JObject, или если n равно null.

Исходный код Редактировать
proc getElems(n: JsonNode; default: seq[JsonNode] = @[]): seq[JsonNode] {...}{.
    raises: [], tags: [].}

Возвращает массив JArray JsonNode.

Возвращает default если n не является JArray, или если n равно null.

Исходный код Редактировать
proc add(father, child: JsonNode) {...}{.raises: [], tags: [].}
Добавляет child в узел JArray father. Исходный код Редактировать
proc add(obj: JsonNode; key: string; val: JsonNode) {...}{.raises: [], tags: [].}
Устанавливает поле из JObject. Исходный код Редактировать
proc `%`(s: string): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JString JsonNode. Исходный код Редактировать
proc `%`(n: uint): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JInt JsonNode. Исходный код Редактировать
proc `%`(n: int): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JInt JsonNode. Исходный код Редактировать
proc `%`(n: BiggestUInt): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JInt JsonNode. Исходный код Редактировать
proc `%`(n: BiggestInt): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JInt JsonNode. Исходный код Редактировать
proc `%`(n: float): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JFloat JsonNode. Исходный код Редактировать
proc `%`(b: bool): JsonNode {...}{.raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JBool JsonNode. Исходный код Редактировать
proc `%`(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode {...}{.
    raises: [], tags: [].}
Универсальный конструктор для данных JSON. Создаёт новый JObject JsonNode Исходный код Редактировать
proc `%`[T](elements: openArray[T]): JsonNode
Универсальный конструктор для данных JSON. Создаёт новый JArray JsonNode Исходный код Редактировать
proc `%`[T](table: Table[string, T] | OrderedTable[string, T]): JsonNode
Универсальный конструктор для данных JSON. Создаёт новый JObject JsonNode. Исходный код Редактировать
proc `%`[T](opt: Option[T]): JsonNode
Универсальный конструктор для данных JSON. Создаёт новый JNull JsonNode если opt пуст, в противном случае делегирует подлежащему значению. Исходный код Редактировать
proc `[]=`(obj: JsonNode; key: string; val: JsonNode) {...}{.inline, raises: [],
    tags: [].}
Устанавливает поле из JObject. Исходный код Редактировать
proc `%`[T: object](o: T): JsonNode
Конструирует JsonNode из кортежей и объектов. Исходный код Редактировать
proc `%`(o: ref object): JsonNode
Универсальный конструктор для данных JSON. Создаёт новый JObject JsonNode Исходный код Редактировать
proc `%`(o: enum): JsonNode
Создаёт JsonNode, представляющий указанное значение перечисления в виде строки. Создаёт новый JString JsonNode. Исходный код Редактировать
proc `==`(a, b: JsonNode): bool {...}{.raises: [KeyError], tags: [].}
Проверка двух узлов на равенство Исходный код Редактировать
proc hash(n: JsonNode): Hash {...}{.raises: [Exception], tags: [RootEffect].}
Вычисление хэша для узла JSON Исходный код Редактировать
proc hash(n: OrderedTable[string, JsonNode]): Hash {...}{.noSideEffect,
    raises: [Exception], tags: [RootEffect].}
Исходный код Редактировать
proc len(n: JsonNode): int {...}{.raises: [], tags: [].}
END_OF_DOCUMENT_MARKER
Если n является JArray, оно возвращает количество элементов. Если n является JObject, оно возвращает количество пар. В противном случае возвращает 0. Источник Редактировать
proc `[]`(node: JsonNode; name: string): JsonNode {...}{.inline, raises: [KeyError],
    tags: [].}
Получает поле из JObject, которое не должно быть nil. Если значение в name не существует, возникает исключение KeyError. Источник Редактировать
proc `[]`(node: JsonNode; index: int): JsonNode {...}{.inline, raises: [], tags: [].}
Получает узел в index в массиве. Результат не определён, если index находится вне границ, но при включённых проверках границ массива это приведёт к исключению. Источник Редактировать
proc hasKey(node: JsonNode; key: string): bool {...}{.raises: [], tags: [].}
Проверяет, существует ли key в node. Источник Редактировать
proc contains(node: JsonNode; key: string): bool {...}{.raises: [], tags: [].}
Проверяет, существует ли key в node. Источник Редактировать
proc contains(node: JsonNode; val: JsonNode): bool {...}{.raises: [KeyError],
    tags: [].}
Проверяет, существует ли val в массиве node. Источник Редактировать
proc `{}`(node: JsonNode; keys: varargs[string]): JsonNode {...}{.raises: [],
    tags: [].}

Проходит по узлу и получает заданное значение. Если какой-либо из ключей не существует, возвращает nil. Также возвращает nil, если одна из промежуточных структур данных не является объектом.

Эта процедура может использоваться для создания древовидных структур на лету (иногда называемых автовивификацией):

Пример:

var myjson = %* {"parent": {"child": {"grandchild": 1}}}
doAssert myjson{"parent", "child", "grandchild"} == newJInt(1)
Источник Редактировать
proc `{}`(node: JsonNode; index: varargs[int]): JsonNode {...}{.raises: [], tags: [].}
Проходит по узлу и получает заданное значение. Если какой-либо из индексов не существует, возвращает nil. Также возвращает nil, если одна из промежуточных структур данных не является массивом. Источник Редактировать
proc getOrDefault(node: JsonNode; key: string): JsonNode {...}{.raises: [], tags: [].}
Получает поле из node. Если node равно nil или не является объектом, или значение в key не существует, возвращает nil Источник Редактировать
proc `{}`(node: JsonNode; key: string): JsonNode {...}{.raises: [], tags: [].}
Получает поле из node. Если node равно nil или не является объектом, или значение в key не существует, возвращает nil Источник Редактировать
proc `{}=`(node: JsonNode; keys: varargs[string]; value: JsonNode) {...}{.
    raises: [KeyError], tags: [].}
Проходит по узлу и пытается установить значение в заданном месте на value. Если какие-либо ключи отсутствуют, они добавляются. Источник Редактировать
proc delete(obj: JsonNode; key: string) {...}{.raises: [KeyError], tags: [].}
Удаляет obj[key]. Источник Редактировать
proc copy(p: JsonNode): JsonNode {...}{.raises: [], tags: [].}
Выполняет глубокую копию a. Источник Редактировать
proc escapeJsonUnquoted(s: string; result: var string) {...}{.raises: [], tags: [].}
Преобразует строку s в её JSON-представление без кавычек. Добавляет в result. Источник Редактировать
proc escapeJsonUnquoted(s: string): string {...}{.raises: [], tags: [].}
Преобразует строку s в её JSON-представление без кавычек. Источник Редактировать
proc escapeJson(s: string; result: var string) {...}{.raises: [], tags: [].}
Преобразует строку s в её JSON-представление с кавычками. Добавляет в result. Источник Редактировать
proc escapeJson(s: string): string {...}{.raises: [], tags: [].}
Преобразует строку s в её JSON-представление с кавычками. Источник Редактировать
proc pretty(node: JsonNode; indent = 2): string {...}{.raises: [], tags: [].}

Возвращает JSON-представление node, с отступами и на нескольких строках.

Аналогично prettyprint в Python.

Пример:

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
  }
}"""
Источник Редактировать
proc toUgly(result: var string; node: JsonNode) {...}{.raises: [], tags: [].}

Преобразует node в его JSON-представление, без учёта удобочитаемости. Предназначено для повышения производительности преобразования строки $.

JSON-представление хранится в переданном result

Это обеспечивает более высокую эффективность, чем процедура pretty, так как не пытается отформатировать результирующий JSON для повышения удобочитаемости.

Источник Редактировать
proc `$`(node: JsonNode): string {...}{.raises: [], tags: [].}
Преобразует node в его JSON-представление на одной строке. Источник Редактировать
proc parseJson(s: Stream; filename: string = ""; rawIntegers = false;
               rawFloats = false): JsonNode {...}{.raises: [IOError, OSError,
    IOError, OSError, JsonParsingError, ValueError, Exception],
    tags: [ReadIOEffect, WriteIOEffect].}
Парсит из потока s в JsonNode. filename необходим только для красивых сообщений об ошибках. Если s содержит дополнительные данные, он поднимет JsonParsingError. Этот поток s закрывается после завершения. Если rawIntegers равно true, целые литералы не будут преобразовываться в поле JInt, а будут сохранены как исходные числа с помощью JString. Если rawFloats равно true, числа с плавающей точкой не будут преобразовываться в поле JFloat, а будут сохранены как исходные числа с помощью JString. Источник Редактировать
proc parseJson(buffer: string; rawIntegers = false; rawFloats = false): JsonNode {...}{.
    raises: [IOError, OSError, JsonParsingError, ValueError, Exception],
    tags: [ReadIOEffect, WriteIOEffect].}
Парсит JSON из buffer. Если buffer содержит дополнительные данные, он поднимет JsonParsingError. Если rawIntegers равно true, целые литералы не будут преобразовываться в поле JInt, а будут сохранены как исходные числа с помощью JString. Если rawFloats равно true, числа с плавающей точкой не будут преобразовываться в поле JFloat, а будут сохранены как исходные числа с помощью JString. Источник Редактировать
proc parseFile(filename: string): JsonNode {...}{.
    raises: [IOError, OSError, JsonParsingError, ValueError, Exception],
    tags: [ReadIOEffect, WriteIOEffect].}
Парсит file в JsonNode. Если file содержит дополнительные данные, он поднимет JsonParsingError. Источник Редактировать
proc to[T](node: JsonNode; t: typedesc[T]): T

Десериализует указанный узел в тип объекта, указанный в параметре.

Известные ограничения:

  • Гетерогенные массивы не поддерживаются.
  • Множества в вариантах объектов не поддерживаются.
  • Аннотации `not nil` не поддерживаются.

Пример:

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]
Источник Редактировать

Итераторы

iterator items(node: JsonNode): JsonNode {...}{.raises: [], tags: [].}
Итератор для элементов node. node должен быть JArray. Исходный код Редактировать
iterator mitems(node: var JsonNode): var JsonNode {...}{.raises: [], tags: [].}
Итератор для элементов node. node должен быть JArray. Элементы можно изменять. Исходный код Редактировать
iterator pairs(node: JsonNode): tuple[key: string, val: JsonNode] {...}{.raises: [],
    tags: [].}
Итератор для дочерних элементов node. node должен быть JObject. Исходный код Редактировать
iterator keys(node: JsonNode): string {...}{.raises: [], tags: [].}
Итератор для ключей в node. node должен быть JObject. Исходный код Редактировать
iterator mpairs(node: var JsonNode): tuple[key: string, val: var JsonNode] {...}{.
    raises: [], tags: [].}
Итератор для дочерних элементов node. node должен быть JObject. Значения могут быть изменены Исходный код Редактировать
iterator parseJsonFragments(s: Stream; filename: string = "";
                            rawIntegers = false; rawFloats = false): JsonNode {...}{.raises: [
    IOError, OSError, IOError, OSError, JsonParsingError, ValueError, Exception],
    tags: [ReadIOEffect, WriteIOEffect].}
Парсит поток s в JsonNodes. filename нужен только для удобных сообщений об ошибках. Фрагменты JSON разделены пробелами. Это может быть существенно быстрее, чем аналогичный цикл for x in splitWhitespace(s): yield parseJson(x). Закрывает поток s после завершения. Если rawIntegers true, целые числа не будут преобразованы в поле JInt, а будут сохранены как исходные числа через JString. Если rawFloats true, числа с плавающей запятой не будут преобразованы в поле JFloat, а будут сохранены как исходные числа через JString. Исходный код Редактировать

Макросы

macro `%*`(x: untyped): untyped
Преобразует выражение в JsonNode напрямую, без необходимости указывать % для каждого элемента. Исходный код Редактировать
macro isRefSkipDistinct(arg: typed): untyped
Только для внутреннего использования, не использовать Исходный код Редактировать

Шаблоны

template `%`(j: JsonNode): JsonNode
Исходный код Редактировать

Экспорт

$, $, $, $, $, $, 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API