Pull to refresh

Усложнённый упрощённый JSON

Reading time7 min
Views15K
Есть такой известный и весьма простой текстовый формат JSON.

JSON формат определяет следующие типы: null, boolean (true, false), number, string, array, object.

А что, если поставить задачу о представлении любых JSON данных с помощью всего 4 типов: number, string, array, object?



Добро пожаловать в ненормальное программирование!
Гость программы: NSNJSON (Not So Normal JSON)!


Содержание


Термины и обозначения
Представления для «простых» типов
Представления для «контейнерных» типов
Восстановление JSON
Восстановление JSON для «простых» типов
Восстановление JSON для «контейнерных» типов
Примеры
Драйверы
Заключение

Попробуем подойти к данной задаче немного формально. В этой статье я буду использовать обозначения взятые с сайта json.org. И для удобства добавлю немного своих.

Термины и обозначения


Введем тип boolean = true | false.

А еще введем тип name, значения которого состовляют подмножество значений типа string — те значения, которые являются корректным именем для поля объекта.

В NSNJSON представлениях используется всего три поля:
  • ttype — тип значения (обязательное поле),
  • vvalue — значение (обязательное поле),
  • nname — имя поля (используется в представлениях полей объекта).


Представления для «простых» типов


К «простым» типам будем относить следующие: null, boolean, string, number.

Определим NSNJSON представление Pnull : nullobject
value -> { "t": "null" }

Определим NSNJSON представление Ptrue : trueobject
value -> { "t": "boolean", "v": 1 }

Определим NSNJSON представление Pfalse : falseobject
value -> { "t": "boolean", "v": 0 }

Определим NSNJSON представление Pstring : stringobject
value -> { "t": "string", "v": value }

Определим NSNJSON представление Pnumber : numberobject
value -> { "t": "number", "v": value }

Определим NSNJSON представление Psimple : «простой» типobject:
Psimple(value) = Pnull(value), если value значение типа null,
Psimple(value) = Ptrue(value), если value значение типа true,
Psimple(value) = Pfalse(value), если value значение типа false,
Psimple(value) = Pstring(value), если value значение типа string,
Psimple(value) = Pnumber(value), если value значение типа number.

Представления для «контейнерных» типов


К «контейнерным» типам будем относить следующие: array, object.

И array и object содержат элементы, поэтому необходимо:
  • во-первых, определить представление для каждого элемента,
  • во-вторых, определить итоговое представление всего «контейнера», на основе представлений его элементов.

Сперва рассмотрим «контейнеры», значения в которых имеют только «простой» тип.

Для начала разберемся с массивами, то есть значениями типа array.

Пусть массив data это значение типа array, тогда можно представить массив следующим образом:
data = (e1, ..., en),
где для всех i = 1, ..., n,
  n — длина массива,
  ei — элемент массива — значение «простого» типа.

Применим функцию представления Psimple к каждому элементу массива data.

dataElementsPresentation = (Psimple(e1), ..., Psimple(en)).

Определим NSNJSON представление Parray : arrayobject
data -> { "t": "array", "v": dataElementsPresentation }

Теперь разберемся с объектами, то есть значениями типа object.

Пусть объект data это значение типа object, тогда можно представить объект следующим образом:
data = { (name1, value1), ..., (namen, valuen) },
где для всех i = 1, ..., n,
  n — количество полей объекта,
  (namei, valuei) — поле объекта,
    namei — имя поля
    valuei — значение поля — значение «простого» типа.

В JSON спецификации сказано, что объект это неупорядоченный набор полей, однако в каждом конкретном случае, мы всегда можем перебрать все поля объекта и пронумеровать их в порядке перебора. Воспользовавшись таким приемом, монжно представить объект data в качестве массива полей без потери общности.

Пусть valuePresentation — результат применения функции представления Psimple к значению value.

Определим NSNJSON представление Pfield : name × valueobject
(name, value) -> { "n": name, "t": valuePresentation.t, "v": valuePresentation.v }

Применим функцию представления Pfield к каждому поля объекта data.

dataFieldsPresentation = (Pfield(name1, value1), ..., Pfield(namen, valuen)).

Определим NSNJSON представление Pobject : objectobject
data -> { "t": "object", "v": dataElementsPresentation }

Определим NSNJSON представление Pcontainer : «контейнерный» типobject:
Pcontainer(value) = Parray(value), если value значение типа array,
Pcontainer(value) = Pobject(value), если value значение типа object.

Наконец, определим итоговое NSNJSON представление Pjson : jsonobject:
Pjson(value) = Psimple(value), если value значение «простого» типа,
Pjson(value) = Pcontainer(value), если value значение «контейнерного» типа.

Если теперь в алгоритмах представления для «контейнерных» типов вместо функции Psimple использовать функцию Pjson, то получим функции представления, которые могут работать с «контейнерами», содержащими любые JSON значения.

Таким образом была построена схема представления любых корректных JSON данных с помощью всего четырех JSON типов: number, string, array, object.

Восстановление JSON


У нас есть JSON, мы из него теперь можем получить NSNSJON.
Но можно ли теперь восстановить исходный JSON?
Можно.

Определим функцию получения типа JSON значения Ptype : objectstring
presentation -> presentation.t

Пусть type — результат применения функции Ptype к представлению presentation.

Восстановление JSON для «простых» типов


Определим функцию восстановления Rnull : objectnull
presentation -> if (type == "null") { return null; }

Определим функцию восстановления Rnumber : objectstring
presentation -> if (type == "string") { return presentation.v; }

Определим функцию восстановления Rnumber : objectnumber
presentation -> if (type == "number") { return presentation.v; }

Определим функцию восстановления Rboolean : objectboolean
presentation -> if (type == "boolean") { return presentation.v != 0; }

Определим функцию восстановления Rsimple : object«простой» тип
Rsimple(presentation) = Rnull(presentation), если type = «null»,
Rsimple(presentation) = Rstring(presentation), если type = «string»,
Rsimple(presentation) = Rnumber(presentation), если type = «number».
Rsimple(presentation) = Rboolean(presentation), если type = «boolean»,

Восстановление JSON для «контейнерных» типов


Также как и ранее, сперва рассмотрим «контейнеры», значения в которых имеют только «простой» тип.

Пусть имеется представление presentation массива data.

Применим функцию восстановления Rsimple к каждому элементу массива presentation.v.
data = (e1, ..., en),
где для всех i = 1, ..., n,
  ei — Rsimple(presentation.v[i]) — элемент массива.

Определим функцию восстановления Rarray : objectarray
presentation -> if (type == "array") { return data; }

Пусть теперь имеется представление presentation объекта data.

Применим функцию восстановления Rsimple к каждому элементу массива presentation.v и используя значение поля n восстановим поля объекта.
data = (e1, ..., en),
где для всех i = 1, ..., n,
  ei — (namei, valuei) — поле объекта,
    namei — presentation.v[i].n — имя поля
    valuei — Rsimple(presentation.v[i]) — значение поля.

Определим функцию восстановления Robject : objectobject
presentation -> if (type == "object") { return data; }

Определим функцию восстановления Rcontainer : object«контейнерный» тип
Rcontainer(presentation) = Rarray(value), если type = «array»,
Rcontainer(presentation) = Robject(value), если type = «object».

И в итоге, определим функцию восстановления Rjson : objectjson:
Rjson(presentation) = Rsimple(presentation), если восстанавливается значение «простого» типа,
Rjson(presentation) = Rcontainer(presentation), если восстанавливается значение «контейнерного» типа.

Если теперь в алгоритмах восстановления для «контейнерных» типов вместо функции Rsimple использовать функцию Rjson, то получим функции представления, которые могут работать с «контейнерами», содержащими любые JSON значения.

Примеры


В заключении, хотелось бы показать несколько простых примеров демонстрирующих применение формата NSNJSON.

примерчики
JSON NSNJSON (Not So Normal JSON)
null
{ "t": "null" }
true
{ "t": "boolean", "v": 1 }
false
{ "t": "boolean", "v": 0 }
213
{ "t": "number", "v": 213 }
"Habrahabr"
{ "t": "string", "v": "Habrahabr" }
[
  null,
  true,
  false,
  213,
  "Habrahabr"
]
{
  "t": "array",
  "v": [
    { "t": "null" },
    { "t": "boolean", "v": 1 },
    { "t": "boolean", "v": 0 },
    { "t": "string", "v": "Habrahabr" }
  ]
}
[
  {
    "userId": 17,
    "status": "online"
  },
  {
    "userId": 19,
    "status": "offline"
  }
]
{
  "t": "array",
  "v": [
    {
      "t": "object",
      "v": [
        {
          "n": "userId",
          "t": "number",
          "v": 17
        },
        {
          "n": "status",
          "t": "string",
          "v": "online"
        }
      ]
    },
    {
      "t": "object",
      "v": [
        {
          "n": "userId",
          "t": "number",
          "v": 19
        },
        {
          "n": "status",
          "t": "string",
          "v": "offline"
        }
      ]
    }
  ]
}
{
    "null_field": null,
    "true_field": true,
    "false_field": false,
    "number_field": 213,
    "string_field": "Habrahabr",
    "array_field": [
        "JSON",
        "NSNJSON"
    ]
}
{
  "t": "object",
  "v": [
    {
      "n": "null_field",
      "t": "null"
    },
    {
      "n": "true_field",
      "t": "boolean",
      "v": 1
    },
    {
      "n": "false_field",
      "t": "boolean",
      "v": 0
    },
    {
      "n": "number_field",
      "t": "number",
      "v": 213
    },
    {
      "n": "string_field",
      "t": "string",
      "v": "Habrahabr"
    },
    {
      "n": "array_field",
      "t": "array",
      "v": [
        {
          "t": "string",
          "v": "JSON"
        },
        {
          "t": "string",
          "v": "NSNJSON"
        }
      ]
    }
  ]
}



Драйверы


Я сделал два небольших драйвера для желающих поиграться с данным форматом.

Драйверы доступны на GitHub на странице проекта: nsnjson!
Драйверы:

Хорошая новость для любителей npmjs.com!
Теперь Node.js драйвер доступен и там!

А если Вы хотите попробовать NSNJSON с помощью Java драйвера, то на странице проекта Вы сможете найти инструкцию как настроить Maven файлик pom.xml, чтобы не скачивать драйвер вручную! :)

Заключение


Мне осталось напомнить, что сегодня гостем программы «Ненормальное программирование» был NSNJSON (Not So Normal JSON)!
Спасибо за внимание!
Tags:
Hubs:
-6
Comments21

Articles