Продолжая тему «Ненормальное программирование и 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.
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); }
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 // 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 и скобочках. Спасибо за Ваше внимание!
ссылка на оригинал статьи http://habrahabr.ru/post/269993/
Добавить комментарий