├── .travis.yml
├── CONTRIBUTORS.md
├── LICENSE.md
├── Makefile
├── README.md
├── icon.png
├── main.js
├── package.js
├── package.json
└── test
└── test.js
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.8
4 | - '0.10'
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | * Rodrigo González ([roro89](https://github.com/roro89))
2 | * ([b123400](https://github.com/b123400))
3 | * ([tomByrer](https://github.com/tomByrer))
4 | * WebGap ([lihe1314](https://github.com/lihe1314))
5 | * Joseph Jung ([ozymandias547](https://github.com/ozymandias547))
6 | * ([lskrabonja](https://github.com/lskrabonja))
7 | * Devin Weaver ([sukima](https://github.com/sukima))
8 | * Craig Andrews ([candrews](https://github.com/candrews))
9 | * Oleg Sklyanchuk ([olegskl](https://github.com/olegskl))
10 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # LICENCE
2 |
3 | The MIT License (MIT)
4 | Copyright (c) 2013 Rodrigo González, Sapienlab
5 |
6 | Permission is hereby granted, free of charge, to any
7 | person obtaining a copy of this software and associated
8 | documentation files (the "Software"), to deal in the
9 | Software without restriction, including without limitation
10 | the rights to use, copy, modify, merge, publish,
11 | distribute, sublicense, and/or sell copies of the
12 | Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice
16 | shall be included in all copies or substantial portions of
17 | the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
20 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
21 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
23 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
25 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TESTS = test/*.js
2 |
3 | test:
4 | @./node_modules/.bin/mocha \
5 | $(TESTS) -R spec
6 |
7 | .PHONY: test bench
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://travis-ci.org/sapienlab/jsonpack)
2 | # jsonpack
3 |
4 | A compression algorithm for JSON
5 |
6 | ## Introduction
7 |
8 | jsonpack is a JavaScript program to pack and unpack JSON data.
9 |
10 | It can compress to 55% of original size if the data has a recursive structure, example
11 | [Earthquake GeoJSON](http://earthquake.usgs.gov/earthquakes/feed/geojson/2.5/month) or
12 | [Twitter API](http://search.twitter.com/search.json?q=Twitter%20API&result_type=mixed).
13 |
14 | This lib works in both Node.js and browsers (older browsers missing [ES5's JSON.stringify](http://caniuse.com/json) support will need a [shim](http://bestiejs.github.io/json3/)).
15 |
16 | **Quick example**
17 | ```javascript
18 | // big JSON
19 | var json = {...}
20 |
21 | // pack the big JSON
22 | var packed = jsonpack.pack(json);
23 |
24 | // do stuff...
25 |
26 | // And then unpack the packed
27 | var json = jsonpack.unpack(packed);
28 | ```
29 |
30 | ## Installation
31 |
32 | **jsonpack** can be installed via [cpm][cpm], [volo][volo] or [npm][npm], or simply [downloaded][download].
33 |
34 | Via cpm:
35 |
36 | ```bash
37 | $ cpm install jsonpack
38 | ```
39 |
40 | Via volo:
41 |
42 | ```bash
43 | $ volo add sapienlab/jsonpack
44 | ```
45 |
46 | Via npm:
47 |
48 | ```bash
49 | $ npm install jsonpack
50 | ```
51 |
52 | ## API
53 |
54 | ### Attributes
55 |
56 | #### jsonpack.JSON
57 | A object that implements the JSON.parse() and JSON.stringify() members.
58 | By default is the native JSON implemented in ECMAscript 5.
59 |
60 | ### Members
61 |
62 | #### jsonpack.pack(json, options)
63 | Retrieve a packed representation of the json
64 |
65 | ** Parameters **
66 |
67 | * json {Object|string}: A valid JSON Object or their string representation
68 | * parameters {[Object]}: A optional object
69 | * verbose (devault is false): If is true, print a log message to the console at each step of packing
70 | * example: `jsonpack.pack(json, { verbose: true });` packs with verbose only
71 | * debug {[boolean=false]}: If is true, return a object with the internal representation of the
72 | parser dictionary and the AST
73 | * example: `jsonpack.pack(json, { debug: true });` packs with debug only
74 |
75 | ** Returns:**
76 |
77 | * string: the packed string representation of the data
78 | * object: if parameters.debug is true
79 |
80 | ##### Examples
81 |
82 | * Example 1: Node.js
83 |
84 | ```javascript
85 | // Example in node.js, read a file with JSON content and save another file
86 | // with the packed representation of that JSON
87 | var jsonpack = require('jsonpack/main'),
88 | fs = require('fs');
89 |
90 | // read a file called myBigJSON.json and execute with
91 | // jsonContent as the content of the file
92 | fs.readFile('../data/bigData.json', 'utf8', function(error, jsonContent) {
93 |
94 | // packed now is a string with the packed version of jsonContent
95 | var packed = jsonpack.pack(jsonContent);
96 |
97 | // save the packed in a file
98 | fs.writeFile('../data/packed.txt', packed);
99 |
100 | });
101 | ```
102 |
103 | * Example 2: Browser/Node.js with AMD
104 |
105 | ```javascript
106 | require(['jsonpack', 'text!../data/bigData.json'], function(jsonpack, jsonContent) {
107 |
108 | // packed the data
109 | var packed = jsonpack.pack(jsonContent);
110 |
111 | // Do stuff with the packed string
112 | console.log(packed);
113 | });
114 | ```
115 |
116 | * Example 3: Browser
117 |
118 | ```html
119 |
120 |
149 | ```
150 |
151 | #### jsonpack.unpack(packed, options)
152 |
153 | Unpack the data in the *packed* parameter
154 |
155 | ** Parameters **
156 |
157 | * packed {string} : The result of call jsonpack.packed(...)
158 | * options {[Object]}: Optional object
159 | * verbose (default: false) print a log message to the console at each step of packing
160 |
161 | ** Return: ** Object, the clone of the original JSON
162 |
163 | ##### Examples
164 |
165 | * Example 1: Node.js
166 |
167 | ```javascript
168 | // Example in node.js, read a file with packed content and save another file
169 | // with the string representation of the original JSON
170 | var jsonpack = require('jsonpack/main'),
171 | fs = require('fs');
172 |
173 | // read a file called packedjson and execute with
174 | // packed as the content of the file
175 | fs.readFile('../data/packed.txt', 'utf8', function(error, packed) {
176 |
177 | // data now is a JavaScript Object of the original JSON
178 | var data = jsonpack.unpack(jsonContent);
179 |
180 | // save the JSON in a file. data is a Javascript Object, so must be
181 | // stringifed (and pretty print the JSON with 2 space indents).
182 | fs.writeFile('../data/unpacked.json', JSON.stringify(data, null, 2));
183 |
184 | });
185 | ```
186 |
187 | * Example 2: Browser/Node.js with AMD
188 |
189 | ```javascript
190 | require(['jsonpack', 'text!../data/packed'], function(jsonpack, packed) {
191 |
192 | // unpacked the data
193 | // json now is a clone of the original JSON
194 | var json = jsonpack.unpack(packed);
195 |
196 | // Do stuff with the JavaScript object
197 | console.log(json);
198 | });
199 |
200 | ```
201 |
202 | * Example 3: Browser
203 |
204 | ```html
205 |
206 |
215 | ```
216 |
217 | ## FAQ
218 | ### This library is stable?
219 | Yes, was tested in Node.js, Chrome and Firefox.
220 |
221 | ### How to contribute?
222 | I'm not a native English speaker, so create a issue or better a pull request for all of my grammatical errors :)
223 | As well, if you have a code issue or suggestion, create a issue, Thanks!
224 |
225 | ### What about the icon?
226 | The icon is a generic (LGPL) icon by David Vignoni - http://www.icon-king.com/
227 |
228 | ## LICENCE
229 |
230 | The MIT License (MIT)
231 | Copyright (c) 2013 Rodrigo González, Sapienlab
232 |
233 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
234 |
235 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
236 |
237 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
238 |
239 | [cpm]: https://github.org/kriszyp/cpm
240 | [volo]: http://volojs.org/
241 | [npm]: http://npmjs.org/
242 | [download]: https://github.com/sapienlab/jsonpack/archive/master.zip
243 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rgcl/jsonpack/adad9ffc76ed1e63e39387bd2f61961fa1a6e2a0/icon.png
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2013, Rodrigo González, Sapienlab All Rights Reserved.
3 | Available via MIT LICENSE. See https://github.com/roro89/jsonpack/blob/master/LICENSE.md for details.
4 | */
5 | (function(define) {
6 |
7 | define([], function() {
8 |
9 | var TOKEN_TRUE = -1;
10 | var TOKEN_FALSE = -2;
11 | var TOKEN_NULL = -3;
12 | var TOKEN_EMPTY_STRING = -4;
13 | var TOKEN_UNDEFINED = -5;
14 |
15 | var pack = function(json, options) {
16 |
17 | // Canonizes the options
18 | options = options || {};
19 |
20 | // A shorthand for debugging
21 | var verbose = options.verbose || false;
22 |
23 | verbose && console.log('Normalize the JSON Object');
24 |
25 | // JSON as Javascript Object (Not string representation)
26 | json = typeof json === 'string' ? this.JSON.parse(json) : json;
27 |
28 | verbose && console.log('Creating a empty dictionary');
29 |
30 | // The dictionary
31 | var dictionary = {
32 | strings : [],
33 | integers : [],
34 | floats : []
35 | };
36 |
37 | verbose && console.log('Creating the AST');
38 |
39 | // The AST
40 | var ast = (function recursiveAstBuilder(item) {
41 |
42 | verbose && console.log('Calling recursiveAstBuilder with ' + this.JSON.stringify(item));
43 |
44 | // The type of the item
45 | var type = typeof item;
46 |
47 | // Case 7: The item is null
48 | if (item === null) {
49 | return {
50 | type : 'null',
51 | index : TOKEN_NULL
52 | };
53 | }
54 |
55 | //add undefined
56 | if (typeof item === 'undefined') {
57 | return {
58 | type : 'undefined',
59 | index : TOKEN_UNDEFINED
60 | };
61 | }
62 |
63 | // Case 1: The item is Array Object
64 | if ( item instanceof Array) {
65 |
66 | // Create a new sub-AST of type Array (@)
67 | var ast = ['@'];
68 |
69 | // Add each items
70 | for (var i in item) {
71 |
72 | if (!item.hasOwnProperty(i)) continue;
73 |
74 | ast.push(recursiveAstBuilder(item[i]));
75 | }
76 |
77 | // And return
78 | return ast;
79 |
80 | }
81 |
82 | // Case 2: The item is Object
83 | if (type === 'object') {
84 |
85 | // Create a new sub-AST of type Object ($)
86 | var ast = ['$'];
87 |
88 | // Add each items
89 | for (var key in item) {
90 |
91 | if (!item.hasOwnProperty(key))
92 | continue;
93 |
94 | ast.push(recursiveAstBuilder(key));
95 | ast.push(recursiveAstBuilder(item[key]));
96 | }
97 |
98 | // And return
99 | return ast;
100 |
101 | }
102 |
103 | // Case 3: The item empty string
104 | if (item === '') {
105 | return {
106 | type : 'empty',
107 | index : TOKEN_EMPTY_STRING
108 | };
109 | }
110 |
111 | // Case 4: The item is String
112 | if (type === 'string') {
113 |
114 | // The index of that word in the dictionary
115 | var index = _indexOf.call(dictionary.strings, item);
116 |
117 | // If not, add to the dictionary and actualize the index
118 | if (index == -1) {
119 | dictionary.strings.push(_encode(item));
120 | index = dictionary.strings.length - 1;
121 | }
122 |
123 | // Return the token
124 | return {
125 | type : 'strings',
126 | index : index
127 | };
128 | }
129 |
130 | // Case 5: The item is integer
131 | if (type === 'number' && item % 1 === 0) {
132 |
133 | // The index of that number in the dictionary
134 | var index = _indexOf.call(dictionary.integers, item);
135 |
136 | // If not, add to the dictionary and actualize the index
137 | if (index == -1) {
138 | dictionary.integers.push(_base10To36(item));
139 | index = dictionary.integers.length - 1;
140 | }
141 |
142 | // Return the token
143 | return {
144 | type : 'integers',
145 | index : index
146 | };
147 | }
148 |
149 | // Case 6: The item is float
150 | if (type === 'number') {
151 | // The index of that number in the dictionary
152 | var index = _indexOf.call(dictionary.floats, item);
153 |
154 | // If not, add to the dictionary and actualize the index
155 | if (index == -1) {
156 | // Float not use base 36
157 | dictionary.floats.push(item);
158 | index = dictionary.floats.length - 1;
159 | }
160 |
161 | // Return the token
162 | return {
163 | type : 'floats',
164 | index : index
165 | };
166 | }
167 |
168 | // Case 7: The item is boolean
169 | if (type === 'boolean') {
170 | return {
171 | type : 'boolean',
172 | index : item ? TOKEN_TRUE : TOKEN_FALSE
173 | };
174 | }
175 |
176 | // Default
177 | throw new Error('Unexpected argument of type ' + typeof (item));
178 |
179 | })(json);
180 |
181 | // A set of shorthands proxies for the length of the dictionaries
182 | var stringLength = dictionary.strings.length;
183 | var integerLength = dictionary.integers.length;
184 | var floatLength = dictionary.floats.length;
185 |
186 | verbose && console.log('Parsing the dictionary');
187 |
188 | // Create a raw dictionary
189 | var packed = dictionary.strings.join('|');
190 | packed += '^' + dictionary.integers.join('|');
191 | packed += '^' + dictionary.floats.join('|');
192 |
193 | verbose && console.log('Parsing the structure');
194 |
195 | // And add the structure
196 | packed += '^' + (function recursiveParser(item) {
197 |
198 | verbose && console.log('Calling a recursiveParser with ' + this.JSON.stringify(item));
199 |
200 | // If the item is Array, then is a object of
201 | // type [object Object] or [object Array]
202 | if ( item instanceof Array) {
203 |
204 | // The packed resulting
205 | var packed = item.shift();
206 |
207 | for (var i in item) {
208 |
209 | if (!item.hasOwnProperty(i))
210 | continue;
211 |
212 | packed += recursiveParser(item[i]) + '|';
213 | }
214 |
215 | return (packed[packed.length - 1] === '|' ? packed.slice(0, -1) : packed) + ']';
216 |
217 | }
218 |
219 | // A shorthand proxies
220 | var type = item.type, index = item.index;
221 |
222 | if (type === 'strings') {
223 | // Just return the base 36 of index
224 | return _base10To36(index);
225 | }
226 |
227 | if (type === 'integers') {
228 | // Return a base 36 of index plus stringLength offset
229 | return _base10To36(stringLength + index);
230 | }
231 |
232 | if (type === 'floats') {
233 | // Return a base 36 of index plus stringLength and integerLength offset
234 | return _base10To36(stringLength + integerLength + index);
235 | }
236 |
237 | if (type === 'boolean') {
238 | return item.index;
239 | }
240 |
241 | if (type === 'null') {
242 | return TOKEN_NULL;
243 | }
244 |
245 | if (type === 'undefined') {
246 | return TOKEN_UNDEFINED;
247 | }
248 |
249 | if (type === 'empty') {
250 | return TOKEN_EMPTY_STRING;
251 | }
252 |
253 | throw new TypeError('The item is alien!');
254 |
255 | })(ast);
256 |
257 | verbose && console.log('Ending parser');
258 |
259 | // If debug, return a internal representation of dictionary and stuff
260 | if (options.debug)
261 | return {
262 | dictionary : dictionary,
263 | ast : ast,
264 | packed : packed
265 | };
266 |
267 | return packed;
268 |
269 | };
270 |
271 | var unpack = function(packed, options) {
272 |
273 | // Canonizes the options
274 | options = options || {};
275 |
276 | // A raw buffer
277 | var rawBuffers = packed.split('^');
278 |
279 | // Create a dictionary
280 | options.verbose && console.log('Building dictionary');
281 | var dictionary = [];
282 |
283 | // Add the strings values
284 | var buffer = rawBuffers[0];
285 | if (buffer !== '') {
286 | buffer = buffer.split('|');
287 | options.verbose && console.log('Parse the strings dictionary');
288 | for (var i=0, n=buffer.length; i