Comparing version 1.1.0 to 1.2.0
@@ -10,3 +10,3 @@ { | ||
"args": [ | ||
"${workspaceFolder}/test/encode_test.js" | ||
"${workspaceFolder}/tests/encode_arr_test.js" | ||
], | ||
@@ -13,0 +13,0 @@ "internalConsoleOptions": "openOnSessionStart" |
{ | ||
"name": "nimnjs", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Schema aware compression of JSON data", | ||
@@ -5,0 +5,0 @@ "main": "nimn.js", |
@@ -25,33 +25,13 @@ # nimnjs-node | ||
var schema = { | ||
"type": "object", | ||
"properties": { | ||
"name": { | ||
"type": "string" | ||
}, | ||
"age": { | ||
"type": "number" | ||
}, | ||
"male": { | ||
"type": "boolean" | ||
}, | ||
"projects": { | ||
"type": "array", | ||
"properties": { | ||
"item": { | ||
"type": "object", | ||
"properties": { | ||
"name": { | ||
"type": "string" | ||
}, | ||
"decription": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
"name": "string", | ||
"age": "number", | ||
"human": "boolean", | ||
"projects": [{ | ||
"name": "string", | ||
"decription": "string" | ||
}] | ||
} | ||
var nimnObj = new nimn(schema); | ||
var nimnInstance = new nimn(); | ||
nimnInstance.addSchema(schema); | ||
@@ -61,3 +41,3 @@ var data = { | ||
"age" : 32, | ||
"male" : true, | ||
"human" : true, | ||
"projects" : [ | ||
@@ -71,4 +51,4 @@ { | ||
var result = nimnObj.encode(data);//Æamitº32ÙÇÆsomeºsome long description | ||
result = nimnObj.getDecoder().decode(result); | ||
var result = nimnInstance.encode(data);//Æamitº32ÙÇÆsomeºsome long description | ||
result = nimnInstance.decode(result); | ||
expect(result).toEqual(data); | ||
@@ -81,8 +61,9 @@ ``` | ||
//generate schema and data | ||
var nimnInstance = new nimn(schema); | ||
nimnInstance.configDataType("date",function(val){ | ||
var nimnInstance = new nimn(); | ||
nimnInstance.addDataHandler("date",function(val){ | ||
return nimnDateparser.parse(val,true,true,true) | ||
},function(val){ | ||
return nimnDateparser.parseBack(val,true,true,true) | ||
return nimnDateparser.parseBack(val,true,true,true) | ||
}); | ||
nimnInstance.addSchema(schema); //add after adding all data handlers | ||
@@ -92,2 +73,21 @@ var nimndata = nimnInstance.encode(data); | ||
Encode enum type | ||
```js | ||
var nimnInstance = new nimn(); | ||
nimnInstance.addDataHandler("status",null,null,{ | ||
"M" : "Married", | ||
"S" : "Single" | ||
}); | ||
nimnInstance.addSchema(schema); //add after adding all data handlers | ||
``` | ||
Just mark a data type | ||
```js | ||
var nimnInstance = new nimn(); | ||
nimnInstance.addDataHandler("image"); | ||
nimnInstance.addSchema(schema); //add after adding all data handlers | ||
``` | ||
Include [dist](dist/nimn.js) in your HTML to use it in browser. | ||
@@ -94,0 +94,0 @@ |
@@ -17,6 +17,6 @@ var char = require("./util").char; | ||
emptyValue: char(178), | ||
yesChar : char(217), | ||
noChar : char(218), | ||
//yesChar : char(217), | ||
//noChar : char(218), | ||
boundryChar : char(186), | ||
arraySepChar: char(197), | ||
arrayEnd: char(197), | ||
objStart: char(198), | ||
@@ -33,5 +33,5 @@ arrStart: char(199) | ||
chars.emptyChar, | ||
chars.yesChar, | ||
chars.noChar, | ||
chars.arraySepChar, | ||
//chars.yesChar, | ||
//chars.noChar, | ||
chars.arrayEnd, | ||
chars.objStart, | ||
@@ -38,0 +38,0 @@ chars.arrStart |
@@ -13,4 +13,6 @@ var chars = require("./chars").chars; | ||
}else{ | ||
switch(schema.type.value){ | ||
case dataType.ARRAY.value: | ||
if(typeof schema === "string"){//premitive | ||
return this.readPremitiveValue(schema); | ||
}else{ | ||
if(Array.isArray(schema)){ | ||
if(this.currentChar() === chars.emptyChar){ | ||
@@ -20,7 +22,6 @@ this.index++; | ||
}else if(this.currentChar() !== chars.arrStart){ | ||
throw Error("Parsing error: Array start char was expcted"); | ||
throw Error("Parsing error: Array start char was expected"); | ||
}else{ | ||
this.index++;//skip array start char | ||
var itemSchema = schema.properties; //schema of array item | ||
var item = getKey(itemSchema,0); | ||
var item = schema[0]; | ||
var obj = [] | ||
@@ -32,6 +33,7 @@ do{ | ||
} | ||
}while(this.dataToDecode[this.index] === chars.arraySepChar && ++this.index); | ||
}while(this.dataToDecode[this.index] !== chars.arrayEnd); | ||
++this.index; | ||
return obj; | ||
} | ||
case dataType.OBJECT.value: | ||
}else{//object | ||
if(this.currentChar() === chars.emptyChar){ | ||
@@ -41,10 +43,9 @@ this.index++; | ||
}else if(this.currentChar() !== chars.objStart){ | ||
throw Error("Parsing error: Object start char was expcted"); | ||
throw Error("Parsing error: Object start char was expected : " + this.currentChar()); | ||
}else{ | ||
this.index++;//skip object start char | ||
var keys = Object.keys(schema.properties); | ||
var keys = Object.keys(schema); | ||
var obj = {}; | ||
//var item = getKey(schema,0); | ||
for(var i in keys){ | ||
var r = this._d(schema.properties[keys[i]]) ; | ||
var r = this._d(schema[keys[i]]) ; | ||
if(r !== undefined){ | ||
@@ -56,6 +57,4 @@ obj[keys[i]] = r; | ||
} | ||
default://premitive | ||
return this.readPremitiveValue(schema); | ||
} | ||
} | ||
} | ||
@@ -74,8 +73,8 @@ | ||
var val = ""; | ||
if(schemaOfCurrentKey.readUntil){ | ||
//if(schemaOfCurrentKey.readUntil){ | ||
val = this.readFieldValue(schemaOfCurrentKey.readUntil); | ||
}else{ | ||
/* }else{ | ||
val = this.currentChar(); | ||
this.index += val.length; | ||
} | ||
} */ | ||
if(val === chars.emptyValue){ | ||
@@ -85,4 +84,4 @@ val = ""; | ||
if(this.currentChar() === chars.boundryChar) this.index++; | ||
return schemaOfCurrentKey.type.parseBack(val); | ||
var dh = this.dataHandlers[schemaOfCurrentKey]; | ||
return dh.parseBack(val); | ||
} | ||
@@ -96,2 +95,7 @@ | ||
decoder.prototype.readFieldValue = function(until){ | ||
until = this.handledChars; | ||
if(until.indexOf(this.dataToDecode[this.index]) !== -1 ){ | ||
return this.dataToDecode[this.index++]; | ||
}else{ | ||
var val = ""; | ||
@@ -101,5 +105,5 @@ var len = this.dataToDecode.length; | ||
for(;this.index < len && until.indexOf(this.dataToDecode[this.index]) === -1;this.index++ ); | ||
for(;this.index < len && until.indexOf(this.dataToDecode[this.index]) === -1;this.index++); | ||
return this.dataToDecode.substr(start, this.index-start); | ||
} | ||
} | ||
@@ -109,2 +113,3 @@ | ||
decoder.prototype.decode = function(objStr){ | ||
this.index= 0; | ||
if(!objStr || typeof objStr !== "string" || objStr.length === 0) throw Error("input should be a valid string"); | ||
@@ -115,6 +120,8 @@ this.dataToDecode = objStr; | ||
function decoder(schema){ | ||
function decoder(schema,dataHandlers,charArr){ | ||
this.schema = schema; | ||
this.index= 0; | ||
this.handledChars = charArr; | ||
this.dataHandlers = dataHandlers; | ||
} | ||
module.exports= decoder; | ||
module.exports = decoder; |
var chars = require("./chars").chars; | ||
var getKey = require("./util").getKey; | ||
var dataType = require("./schema").dataType; | ||
var DataType = require("./schema").DataType; | ||
var charsArr = require("./chars").charsArr; | ||
var encode = function(jObj,e_schema){ | ||
if(e_schema.type.value > 4){ | ||
//case dataType.ARRAY.value: | ||
//case dataType.OBJECT.value: | ||
var hasValidData = hasData(jObj); | ||
if(hasValidData === true){ | ||
var str = ""; | ||
if(e_schema.type.value === 6){ | ||
str += chars.arrStart; | ||
var itemSchema = getKey(e_schema.properties,0); | ||
//var itemSchemaType = itemSchema.type; | ||
var arr_len = jObj.length; | ||
for(var arr_i=0;arr_i < arr_len;){ | ||
var r; | ||
/* if(itemSchema.type.value < 5 ){//comment to avoid recursion | ||
r = getValue(jObj[arr_i],itemSchema.type); | ||
}else{ */ | ||
r = encode(jObj[arr_i],itemSchema) ; | ||
//} | ||
str = processValue(str,r); | ||
if(arr_len > ++arr_i){ | ||
str += chars.arraySepChar; | ||
} | ||
} | ||
}else{//object | ||
str += chars.objStart; | ||
var keys = Object.keys(e_schema.properties); | ||
for(var i in keys){ | ||
var r; | ||
/* if(e_schema.properties[keys[i]].type.value < 5 ){//comment to avoid recursion | ||
r = getValue(jObj[keys[i]],e_schema.properties[keys[i]].type); | ||
}else { */ | ||
r = encode(jObj[keys[i]],e_schema.properties[keys[i]]) ; | ||
//} | ||
str = processValue(str,r); | ||
} | ||
Encoder.prototype._e = function(jObj,e_schema){ | ||
if(typeof e_schema === "string"){//premitive | ||
return this.getValue(jObj,e_schema); | ||
}else{ | ||
var hasValidData = hasData(jObj); | ||
if(hasValidData === true){ | ||
var str = ""; | ||
if(Array.isArray(e_schema)){ | ||
str += chars.arrStart; | ||
var itemSchema = e_schema[0]; | ||
//var itemSchemaType = itemSchema; | ||
var arr_len = jObj.length; | ||
for(var arr_i=0;arr_i < arr_len;arr_i++){ | ||
var r = this._e(jObj[arr_i],itemSchema) ; | ||
str = this.processValue(str,r); | ||
/* if(arr_len > ++arr_i){ | ||
str += chars.arraySepChar; | ||
} */ | ||
} | ||
return str; | ||
}else{ | ||
return hasValidData; | ||
str += chars.arrayEnd;//indicates that next item is not array item | ||
}else{//object | ||
str += chars.objStart; | ||
var keys = Object.keys(e_schema); | ||
for(var i in keys){ | ||
var key = keys[i]; | ||
var r = this._e(jObj[key],e_schema[key]) ; | ||
str = this.processValue(str,r); | ||
} | ||
} | ||
}else{//premitive | ||
return getValue(jObj,e_schema.type); | ||
return str; | ||
}else{ | ||
return hasValidData; | ||
} | ||
} | ||
} | ||
function processValue(str,r){ | ||
if(!isAppChar(r[0]) && !isAppChar(str[str.length -1])){ | ||
Encoder.prototype.processValue= function(str,r){ | ||
if(!this.isAppChar(r[0]) && !this.isAppChar(str[str.length -1])){ | ||
str += chars.boundryChar; | ||
@@ -60,7 +50,14 @@ } | ||
var getValue= function(a,type){ | ||
/** | ||
* | ||
* @param {*} a | ||
* @param {*} type | ||
* @return {string} return either the parsed value or a special char representing the value | ||
*/ | ||
Encoder.prototype.getValue= function(a,type){ | ||
if(a === undefined) return chars.missingPremitive; | ||
else if(a === null) return chars.nilPremitive; | ||
else if( a === "") return chars.emptyValue; | ||
else return type.parse(a); | ||
else return this.dataHandlers[type].parse(a); | ||
//else return type.parse(a); | ||
} | ||
@@ -82,6 +79,16 @@ | ||
function isAppChar(ch){ | ||
return charsArr.indexOf(ch) !== -1; | ||
Encoder.prototype.isAppChar = function(ch){ | ||
return this.handledChars.indexOf(ch) !== -1; | ||
} | ||
exports.encode= encode; | ||
Encoder.prototype.encode = function(jObj){ | ||
return this._e(jObj,this.schema); | ||
} | ||
function Encoder(schema,dHandlers, charArr){ | ||
this.dataHandlers = dHandlers; | ||
this.handledChars = charArr; | ||
this.schema = schema; | ||
} | ||
module.exports = Encoder; |
106
src/nimn.js
var boolean = require("./parsers/boolean"); | ||
var numParser = require("./parsers/number"); | ||
var dataType = require("./schema").dataType; | ||
var updateSchema = require("./schema_updater").updateSchema; | ||
var decoder = require("./decoder"); | ||
var encode = require("./encoder").encode; | ||
var chars = require("./chars").chars; | ||
var appCharsArr = require("./chars").charsArr; | ||
var helper = require("./helper"); | ||
var Decoder = require("./decoder"); | ||
var Encoder = require("./encoder"); | ||
var DataHandler = require("./DataHandler"); | ||
function nimn(schema) { | ||
this.configDataType("boolean",boolean.parse,boolean.parseBack); | ||
this.configDataType("string",returnBack,returnBack); | ||
this.configDataType("number",numParser.parse,numParser.parseBack); | ||
this.configDataType("date",returnBack,returnBack); | ||
this.configDataType("object",returnBack,returnBack); | ||
this.configDataType("array",returnBack,returnBack); | ||
function nimn() { | ||
this.handledChars = appCharsArr.slice(); | ||
this.dataHandlers = {}; | ||
this.addDataHandler("boolean",null,null,boolean.charset,true); | ||
//this.addDataHandler("boolean",boolean.parse,boolean.parseBack,boolean.charset,true); | ||
this.addDataHandler("string"); | ||
this.addDataHandler("number",numParser.parse, numParser.parseBack); | ||
this.addDataHandler("date"); | ||
} | ||
this.e_schema = Object.assign({},schema); | ||
updateSchema(this.e_schema); | ||
/** | ||
* This method should be called once all the data handlers are registered. | ||
* It updates internal schema based on given schema. | ||
* | ||
* @example | ||
* { | ||
* "field1" : "string", | ||
* "field2" : "date", | ||
* "field3" : { | ||
* "field4" : "number" | ||
* }, | ||
* "field5" : [ "image"], | ||
* "field6" : [{ "field7" : "boolean"}] | ||
* } | ||
* @param {*} schema | ||
* @returns {void} | ||
*/ | ||
nimn.prototype.addSchema= function(schema){ | ||
this.schema = JSON.parse(JSON.stringify(schema)); | ||
helper.validateSchema(schema,this.dataHandlers); | ||
this.encoder = new Encoder(this.schema,this.dataHandlers,this.handledChars); | ||
} | ||
function returnBack(a){ return a} | ||
//function returnCallBack(a,callBack){ callBack(a)} | ||
nimn.prototype.getDecoder= function(){ | ||
return new decoder(this.e_schema); | ||
} | ||
/** | ||
* To register encoder and decoder for specific data type. | ||
* You can update existnig handler od add new using this method. | ||
* "string", "number", "boolean", and "date" are handled by default. | ||
* | ||
* Encoder function accepts value as parameter | ||
* | ||
* Decoder function accepts value as a first parameter and callBack function as second optional parameter | ||
* charset should be set when given type should be treated as enum or fixed set of values | ||
* @example | ||
* //to map | ||
* nimnInstance.addDataHandler("status",null,null,{ "R": "running", "S" : "stop", "I", "ready to run"},false) | ||
* @example | ||
* //just for identification | ||
* nimnInstance.addDataHandler("image"); | ||
* @example | ||
* //to compress more | ||
* nimnInstance.addDataHandler("date", datecompressor.parse, datecompressor.parseBack); | ||
* @param {string} type | ||
* @param {function} parseWith | ||
* @param {function} parseBackWith | ||
* @param {function} parseWith - will be used by encoder to encode given type's value | ||
* @param {function} parseBackWith - will be used by decoder to decode given type's value | ||
* @param {Object} charset - map of charset and fixed values | ||
* @param {boolean} [dontSeparateWIthBoundaryChar=false] - if true encoder will not separate given type's value with boundary char | ||
*/ | ||
nimn.prototype.configDataType = function(type,parseWith,parseBackWith){ | ||
var dType = dataType.getType(type); | ||
dType.parse = parseWith; | ||
dType.parseBack = parseBackWith; | ||
nimn.prototype.addDataHandler = function(type,parseWith,parseBackWith,charset,dontSeparateWIthBoundaryChar){ | ||
var dataHandler = new DataHandler(type,/* parseWith,parseBackWith, */charset,dontSeparateWIthBoundaryChar); | ||
if(parseWith) dataHandler.parse = parseWith; | ||
if(parseBackWith) dataHandler.parseBack = parseBackWith; | ||
//unque charset don't require boundary char. Hence check them is they are already added | ||
if(dontSeparateWIthBoundaryChar && charset){ | ||
var keys = Object.keys(charset); | ||
for(var k in keys){ | ||
var ch = keys[k]; | ||
if(this.handledChars.indexOf(ch) !== -1){ | ||
throw Error("Charset Error: "+ ch +" is not allowed. Either it is reserved or being used by another data handler"); | ||
}else{ | ||
this.handledChars.push(ch); | ||
} | ||
} | ||
} | ||
this.dataHandlers[type] = dataHandler; | ||
} | ||
nimn.prototype.encode = function(jObj){ | ||
return encode(jObj,this.e_schema) | ||
return this.encoder.encode(jObj); | ||
} | ||
nimn.prototype.decode= function(encodedVal){ | ||
var decoder = new Decoder(this.schema,this.dataHandlers,this.handledChars); | ||
return decoder.decode(encodedVal); | ||
} | ||
module.exports = nimn; |
var chars = require("../chars").chars; | ||
var char = require("../util").char; | ||
function parse(val){ | ||
return val ? chars.yesChar : chars.noChar; | ||
return val ? yes : no; | ||
} | ||
function parseBack(val){ | ||
return val === chars.yesChar ? true : false; | ||
return val === yes ? true : false; | ||
} | ||
var yes = char(217); | ||
var no = char(218); | ||
booleanCharset = {}; | ||
booleanCharset[yes] = true; | ||
booleanCharset[no] = false; | ||
exports.parse = parse; | ||
exports.parseBack = parseBack; | ||
exports.charset = booleanCharset; |
@@ -6,4 +6,2 @@ var dataType = { | ||
BOOLEAN : { value: 4}, | ||
OBJECT : { value: 5}, | ||
ARRAY : { value: 6}, | ||
getType(str){ | ||
@@ -15,8 +13,16 @@ switch(str){ | ||
case "boolean": return dataType.BOOLEAN; | ||
case "object": return dataType.OBJECT; | ||
case "array": return dataType.ARRAY; | ||
} | ||
}, | ||
getInstance(str){ | ||
return new DataType(dataType.getType(str)); | ||
} | ||
} | ||
exports.dataType = dataType; | ||
function DataType(type){ | ||
this.value = type.value; | ||
this.parse = type.parse; | ||
this.parseBack = type.parseBack; | ||
} | ||
exports.dataType = dataType; | ||
21453
15
469