Продолжая тему «Ненормальное программирование и JSON» меня посетила еще одна мысль об еще одном представлении JSON данных.
В своей прошлой статье «Усложнённый упрощённый JSON» я уже рассказал о формате NSNJSON, который позволяет представить любые JSON данные с помощью всего 4 JSON типов: number, string, array, object.
На этот раз я расскажу о способе представления любых JSON данных с помощью всего 3 типов: number, string, array.
История
Проект «Brackets»
Реализация
Примерчики
После публикации статьи о формате NSNJSON я начал работать над драйверами для этого формата. Практически сразу же у меня возникла идея предоставления пользователям драйвера возможность использовать свои правила для кодирования/декодирования JSON.
В ходе работы над этой идеей я пытался придумать интересный пример пользовательских правил… И вот однажды, меня и посетила это безумная мысль под кодовым названием «Brackets».
Я подумал, что было бы интересно посмотреть на JSON данные, которые представлены с помощью строк, чисел и скобочек!
Для начала введем таблицу констант для JSON типов.
Для типа null всё очень просто. Просто отображаем его на пустой массив.
Определим представление для значения value типа string:
Определим представление для значения value типа string:
Определим представление для значения value типа boolean (true, false):
Остались массивы и объекты. Начну с массивов. Для массивов можно определить представление так:
С объектами чуть сложнее, ведь необходимо сохранять информацию о полях объекта.
Сначала определим представление для поля объекта.
Пусть поле объекта имеет имя name и значение value. Тогда представление определим так:
Теперь можно определить представление для всего объекта целиком:
Определив такие представления очень легко определить и алгоритм восстановления JSON.
— В случае, если мы сталкиваемся с пустым массивом, — значит встретили представление значения типа null.
— Иначе, берем первый элемент массива и ищем по нему соотвествующее имя JSON типа. Далее, драйвер в соответствии с полученным типом вызовет нужную функцию декодирования.
Я покажу реализацию такого представления с помощью NSNJSON Node.js драйвера.
Драйвер опубликован на npmjs.com, поэтому для установки драйвера достаточно выполнить простую команду:
Перед тем, как я начну рассказывать о реализации, хочу отметить, что драйвер использует Maybe из пакета data.maybe.
Для начала определим два файла констант.
Константы маркеров (custom.format.json):
Константы имен JSON типов (nsnjson.types.json):
Теперь необходимо задать собственные правила представления/восстановления JSON.
Ну а теперь посмотрим как этим пользоваться.
По традиции, привожу примерчики применения данного алгоритма представления JSON данных.
Ну вот и подошла к концу статья о ненормальном программировании, JSON и скобочках. Спасибо за Ваше внимание!
В своей прошлой статье «Усложнённый упрощённый JSON» я уже рассказал о формате NSNJSON, который позволяет представить любые JSON данные с помощью всего 4 JSON типов: number, string, array, object.
На этот раз я расскажу о способе представления любых JSON данных с помощью всего 3 типов: number, string, array.
Содержание
История
Проект «Brackets»
Реализация
Примерчики
История
После публикации статьи о формате NSNJSON я начал работать над драйверами для этого формата. Практически сразу же у меня возникла идея предоставления пользователям драйвера возможность использовать свои правила для кодирования/декодирования JSON.
В ходе работы над этой идеей я пытался придумать интересный пример пользовательских правил… И вот однажды, меня и посетила это безумная мысль под кодовым названием «Brackets».
Я подумал, что было бы интересно посмотреть на JSON данные, которые представлены с помощью строк, чисел и скобочек!
Проект «Brackets»
Для начала введем таблицу констант для JSON типов.
JSON тип | number | string | true | false | array | object |
---|---|---|---|---|---|---|
Маркер | 1 | 2 | 3 | 3 | 4 | 5 |
Для типа null всё очень просто. Просто отображаем его на пустой массив.
null -> []
Определим представление для значения value типа string:
value -> [1, value]
Определим представление для значения value типа string:
value -> [2, value]
Определим представление для значения value типа boolean (true, false):
value -> [3, ~~value]
// [3, 1] для true
// [3, 0] для false
Остались массивы и объекты. Начну с массивов. Для массивов можно определить представление так:
array -> [4, p1, ..., pN]
// p1, ..., pN - представления для элементов массива
С объектами чуть сложнее, ведь необходимо сохранять информацию о полях объекта.
Сначала определим представление для поля объекта.
Пусть поле объекта имеет имя name и значение value. Тогда представление определим так:
name: value -> [name, valuePresentation]
// valuePresentation - представление значения value
Теперь можно определить представление для всего объекта целиком:
name: value -> [5, [name1, value1Presentation], ..., [nameN, valueNPresentation]]
// value1Presentation, ..., valueNPresentation - представления для значений value1, ..., valueN
Определив такие представления очень легко определить и алгоритм восстановления JSON.
— В случае, если мы сталкиваемся с пустым массивом, — значит встретили представление значения типа null.
— Иначе, берем первый элемент массива и ищем по нему соотвествующее имя JSON типа. Далее, драйвер в соответствии с полученным типом вызовет нужную функцию декодирования.
Реализация
Я покажу реализацию такого представления с помощью NSNJSON Node.js драйвера.
Драйвер опубликован на npmjs.com, поэтому для установки драйвера достаточно выполнить простую команду:
npm install nsnjson-driver
Перед тем, как я начну рассказывать о реализации, хочу отметить, что драйвер использует Maybe из пакета data.maybe.
Для начала определим два файла констант.
Константы маркеров (custom.format.json):
{
"TYPE_MARKER_NUMBER": 1,
"TYPE_MARKER_STRING": 2,
"TYPE_MARKER_BOOLEAN": 3,
"TYPE_MARKER_ARRAY": 4,
"TYPE_MARKER_OBJECT": 5
}
Константы имен JSON типов (nsnjson.types.json):
{
"NULL": "null",
"NUMBER": "number",
"STRING": "string",
"BOOLEAN": "boolean",
"ARRAY": "array",
"OBJECT": "object"
}
Теперь необходимо задать собственные правила представления/восстановления JSON.
custom.encoder.js
var Maybe = require('data.maybe');
var nsnjson = require('nsnjson-driver');
var Types = require('./nsnjson.types');
var Format = require('./custom.format');
var encodingOptions = {};
// Определяем реализацию правила представления для значения value типа JSON null
encodingOptions[Types.NULL] = function() {
return Maybe.Just([]);
}
// Определяем реализацию правила представления для значения value типа JSON number
encodingOptions[Types.NUMBER] = function(value) {
return Maybe.Just([Format.TYPE_MARKER_NUMBER, value]);
}
// Определяем реализацию правила представления для значения value типа JSON string
encodingOptions[Types.STRING] = function(value) {
return Maybe.Just([Format.TYPE_MARKER_STRING, value]);
}
// Определяем реализацию правила представления для значения value типа JSON boolean
encodingOptions[Types.BOOLEAN] = function(value) {
return Maybe.Just([Format.TYPE_MARKER_BOOLEAN, ~~value]);
}
// Определяем реализацию правила представления для значения array типа JSON array
encodingOptions[Types.ARRAY] = function(array) {
var presentation = [Format.TYPE_MARKER_ARRAY];
for (var i = 0, size = array.length; i < size; i++) {
var itemPresentationMaybe = this.encode(array[i]);
if (itemPresentationMaybe.isJust) {
var itemPresentation = itemPresentationMaybe.get();
presentation.push(itemPresentation);
}
}
return Maybe.Just(presentation);
}
// Определяем реализацию правила представления для значения object типа JSON object
encodingOptions[Types.OBJECT] = function(object) {
var presentation = [Format.TYPE_MARKER_OBJECT];
for (var name in object) {
if (object.hasOwnProperty(name)) {
var valuePresentationMaybe = this.encode(object[name]);
if (valuePresentationMaybe.isJust) {
var valuePresentation = valuePresentationMaybe.get();
var fieldPresentation = [name, valuePresentation];
presentation.push(fieldPresentation);
}
}
}
return Maybe.Just(presentation);
}
custom.decoder.js
var Maybe = require('data.maybe');
var nsnjson = require('nsnjson-driver');
var Types = require('./nsnjson.types');
var Format = require('./custom.format')
var decodingOptions = {
// Определяем функцию получения имени JSON типа из представления.
'type': function(presentation) {
if (presentation.length == 0) {
return Maybe.Just(Types.NULL);
} else {
switch (presentation[0]) {
case Format.TYPE_MARKER_NUMBER: return Maybe.Just(Types.NUMBER);
case Format.TYPE_MARKER_STRING: return Maybe.Just(Types.STRING);
case Format.TYPE_MARKER_BOOLEAN: return Maybe.Just(Types.BOOLEAN);
case Format.TYPE_MARKER_ARRAY: return Maybe.Just(Types.ARRAY);
case Format.TYPE_MARKER_OBJECT: return Maybe.Just(Types.OBJECT);
}
return Maybe.Nothing();
}
}
};
// Определяем восстановление значения типа JSON null
decodingOptions[Types.NULL] = function() {
return Maybe.Just(null);
}
// Определяем восстановление значения типа JSON number
decodingOptions[Types.NUMBER] = function(presentation) {
return Maybe.Just(presentation[1]);
}
// Определяем восстановление значения типа JSON string
decodingOptions[Types.STRING] = function(presentation) {
return Maybe.Just(presentation[1]);
}
// Определяем восстановление значения типа JSON boolean
decodingOptions[Types.BOOLEAN] = function(presentation) {
return Maybe.Just(presentation[1] != 0);
}
// Определяем восстановление значения типа JSON array
decodingOptions[Types.ARRAY] = function(presentation) {
var array = [];
for (var i = 1, size = presentation.length; i < size; i++) {
var itemPresentation = presentation[i];
var itemMaybe = this.decode(itemPresentation);
if (itemMaybe.isJust) {
var item = itemMaybe.get();
array.push(item);
}
}
return Maybe.Just(array);
}
// Определяем восстановление значения типа JSON object
decodingOptions[Types.OBJECT] = function(presentation) {
var object = {};
for (var i = 1, size = presentation.length; i < size; i++) {
var fieldPresentation = presentation[i];
var name = fieldPresentation[0];
var valueMaybe = this.decode(fieldPresentation[1]);
if (valueMaybe.isJust) {
var value = valueMaybe.get();
object[name] = value;
}
}
return Maybe.Just(object);
}
module.exports = {
decode: function(presentation) {
return nsnjson.decode(presentation, decodingOptions);
}
};
Ну а теперь посмотрим как этим пользоваться.
// Загружаем encoder с нашими правилами представления JSON
var customEncoder = require('./custom.encoder');
// Загружаем decoder с нашими правилами восстановления JSON
var customDecoder = require('./custom.decoder');
var data = {message: ['I', 'love', 'brackets']};
console.log('Data:', JSON.stringify(data));
// Data: { "message": [ "I", "love", "brackets" ] }
var presentationMaybe = customEncoder.encode(data);
if (presentationMaybe.isJust) {
var presentation = presentationMaybe.get();
console.log('Presentation:', JSON.stringify(presentation));
// Presentation: [ 5, [ "message", [ 4, [ 2, "I" ], [ 2, "love" ], [ 2, "brackets" ] ] ] ]
var restoredDataMaybe = customDecoder.decode(presentation);
if (restoredDataMaybe.isJust) {
var restoredData = restoredDataMaybe.get();
console.log('Restored data:', JSON.stringify(restoredData));
// Restored data: { "message": [ "I", "love", "brackets" ] }
}
}
Примерчики
По традиции, привожу примерчики применения данного алгоритма представления JSON данных.
Простые примерчики
JSON тип null
JSON тип number
JSON тип boolean
JSON тип string
JSON тип array
JSON тип object
// JSON
null
// NSNJSON
[ ]
JSON тип number
// JSON
2015
// NSNJSON
[ 1, 2015 ]
JSON тип boolean
// JSON
true
// NSNJSON
[ 3, 1 ]
JSON тип string
// JSON
"Habrahabr.ru"
// NSNJSON
[ 2, "Habrahabr.ru" ]
JSON тип array
// JSON
[ "Year", 2015 ]
// NSNJSON
[ 4, [ 2, "Year" ], [ 1, 2015 ] ]
JSON тип object
// JSON
{ "message": [ "I", "love", "brackets" ] }
// NSNJSON
[ 5, [ "message", [ 4, [ 2, "I" ], [ 2, "love" ], [ 2, "brackets" ] ] ] ]
Ну вот и подошла к концу статья о ненормальном программировании, JSON и скобочках. Спасибо за Ваше внимание!
yatagarasu
Вы в одном шаге от лиспа.
jxcoder
:) Вроде здорово! :)
yatagarasu
Да, ну в общем интересное занятие. Делайте уже лисп машину с синтаксисом json.
vintage
А я вот делаю лисп машину с синтаксисом tree, получается симпатишненько)
YChebotaev
Скорее, от msgpack-а.
yatagarasu
msgpack тоже в одном шаге от лиспа.