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 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/