📜 ⬆️ ⬇️

JSON for fans of braces

Continuing on the topic “Abnormal Programming and JSON”, another thought came to me about another representation of JSON data.

In my last article, “Complicated Simplified JSON”, I already talked about the NSNJSON format, which allows you to present any JSON data using only 4 JSON types: number , string , array , object .

This time I will talk about the method of representing any JSON data using only 3 types: number , string , array .
')



Content


Story
Project "Brackets"
Implementation
Fittings


Story


After publishing an article on the NSNJSON format, I started working on drivers for this format. Almost immediately, I had the idea of ​​giving the driver user the opportunity to use their rules for encoding / decoding JSON.

In the course of working on this idea, I tried to come up with an interesting example of user rules ... And then one day, I was visited by this crazy thought, codenamed "Brackets" .

I thought it would be interesting to look at the JSON data that is represented using strings, numbers and parentheses!

Project "Brackets"


First we introduce a table of constants for JSON types.
Json typenumberstringtruefalsearrayobject
Markerone233fourfive

For the null type, everything is very simple. Just display it on an empty array.
null -> [] 

Define the representation for the value of the string type:
 value -> [1, value] 

Define the representation for the value of the string type:
 value -> [2, value] 

Define the representation for the value of type boolean ( true , false ):
 value -> [3, ~~value] // [3, 1]  true // [3, 0]  false 

Arrays and objects remained. I'll start with arrays. For arrays, you can define a view like this:
 array -> [4, p1, ..., pN] // p1, ..., pN -     

It is a bit more difficult with objects, because it is necessary to save information about the fields of an object.
First, we define the representation for the object field.
Let the object field be named and value value . Then we define the representation as:
 name: value -> [name, valuePresentation] // valuePresentation -   value 

Now you can define a view for the entire object:
 name: value -> [5, [name1, value1Presentation], ..., [nameN, valueNPresentation]] // value1Presentation, ..., valueNPresentation -    value1, ..., valueN 

Having defined such representations is very easy to determine and the JSON recovery algorithm.
- In case we encounter an empty array, it means we have met the representation of a value of type null .
- Otherwise, take the first element of the array and look for the corresponding name of the JSON type on it. Further, the driver according to the type received will call the desired decoding function.

Implementation


I will show the implementation of such a view using the NSNJSON Node.js driver .

The driver is published on npmjs.com , so to install the driver, just run a simple command:
 npm install nsnjson-driver 

Before I start talking about the implementation, I want to note that the driver uses Maybe from the data.maybe package.

To begin with, we define two constant files.

Marker Constants ( custom.format.json ):
 { "TYPE_MARKER_NUMBER": 1, "TYPE_MARKER_STRING": 2, "TYPE_MARKER_BOOLEAN": 3, "TYPE_MARKER_ARRAY": 4, "TYPE_MARKER_OBJECT": 5 } 

JSON type naming constants ( nsnjson.types.json ):
 { "NULL": "null", "NUMBER": "number", "STRING": "string", "BOOLEAN": "boolean", "ARRAY": "array", "OBJECT": "object" } 

Now you need to set your own rules for JSON submission / recovery.

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); } }; 



Well, now let's see how to use it.
 //  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" ] } } } 



Fittings


By tradition, I give examples of using this algorithm for presenting JSON data.

Simple examples
JSON type null
 // JSON null // NSNJSON [ ] 

JSON type number
 // JSON 2015 // NSNJSON [ 1, 2015 ] 

JSON type boolean
 // JSON true // NSNJSON [ 3, 1 ] 

JSON type string
 // JSON "Habrahabr.ru" // NSNJSON [ 2, "Habrahabr.ru" ] 

JSON type array
 // JSON [ "Year", 2015 ] // NSNJSON [ 4, [ 2, "Year" ], [ 1, 2015 ] ] 

JSON type object
 // JSON { "message": [ "I", "love", "brackets" ] } // NSNJSON [ 5, [ "message", [ 4, [ 2, "I" ], [ 2, "love" ], [ 2, "brackets" ] ] ] ] 


Well, the article about abnormal programming, JSON and parentheses came to an end. Thank you for your attention!

Source: https://habr.com/ru/post/269993/


All Articles