Skip to content

Instantly share code, notes, and snippets.

@mbarkhau
Last active December 26, 2015 13:31
Show Gist options
  • Save mbarkhau/5026379 to your computer and use it in GitHub Desktop.
Save mbarkhau/5026379 to your computer and use it in GitHub Desktop.
KSON: Keyless Schemafied Object Notation
/* KSON: Keyless Schemafied Object Notation
*
* Version: 0.1 Alpha
*
* A serialization format with two goals in mind:
* 1. Easily parsable using minimal javascript.
* 2. Reduce serialized size compared to JSON.
*
* 1. is accomplished by using the (comparativly fast) JSON parse/stringify
* functions, thus reducing the task of KSON to packing/unpacking the values
* according to a predefined schema. This also greatly reduces the size of
* the downloaded script, compared with other serialization formats
* (gzipped KSON is < 1K).
*
* 2. is accomplished by eliminating often redundant keys and by extending
* native JSON datatypes with an encoding/decoding mechanism. Codecs allow
* for a compact serialized data representation.
*/
!(function (global) {
var DECODERS = {}, ENCODERS = {}, SCHEMAS = {};
function addSchema(schema) {
if (typeof schema === 'string') {
schema = parse(schema);
}
SCHEMAS[schema.id] = schema;
}
function addCodec(name, decoder, encoder){
DECODERS[name] = decoder;
ENCODERS[name] = encoder;
}
function parse(raw, schema_id) {
var data = (typeof raw === 'string') ? JSON.parse(raw) : raw,
data_length = data.length,
is_array, meta_is_subarray,
result, tmp_obj, val,
decoder, schema, meta_id,
i = 0, j, k;
if (!schema_id) {
schema_id = data[0];
i = 1;
}
is_array = schema_id[0] === "[";
if (is_array) {
schema_id = schema_id.slice(2);
result = [];
}
schema = SCHEMAS[schema_id];
var fields = schema.fields,
meta = schema.meta,
fields_length = fields.length;
for (; i < data_length; i += fields_length) {
tmp_obj = {};
for (j = 0; j < fields_length; j++) {
val = data[i + j];
if (val !== null && val !== undefined) {
meta_id = meta[j];
meta_is_subarray = meta_id && meta_id[0] === "[";
if (meta_is_subarray) {
meta_id = meta_id.slice(2);
}
decoder = DECODERS[meta_id];
schema = SCHEMAS[meta_id];
if (schema) {
val = parse(val, meta[j]);
} else if (decoder) {
if (meta_is_subarray) {
for (k = val.length - 1; k >= 0; k--) {
val[k] = decoder(val[k]);
}
} else {
val = decoder(val);
}
}
}
tmp_obj[fields[j]] = val;
}
if (is_array) {
result.push(tmp_obj);
} else {
return tmp_obj;
}
}
return result;
}
function stringify(data, schema_id, is_subarray) {
var is_array = schema_id[0] === "[",
schema_id = is_array ? schema_id.slice(2) : schema_id,
schema = SCHEMAS[schema_id],
fields = schema.fields,
fields_length = fields.length,
meta = schema.meta, meta_id, meta_is_subarray,
i, j, k, obj, val, encoder,
result = [];
if (!is_subarray) {
result[0] = (is_array) ? "[]" + schema_id : schema_id;
}
if (!is_array) {
data = [data];
}
var data_length = data.length;
for (i = 0; i < data_length; i++) {
obj = data[i];
for (j = 0; j < fields_length; j++) {
val = obj[fields[j]];
if (val !== null && val !== undefined) {
meta_id = meta[j];
meta_is_subarray = meta_id && meta_id[0] === "[";
if (meta_is_subarray) {
meta_id = meta_id.slice(2);
}
encoder = ENCODERS[meta_id];
schema = SCHEMAS[meta_id];
if (schema) {
val = stringify(val, meta[j], 1);
} else if (encoder) {
if (meta_is_subarray) {
for (k = val.length - 1; k >= 0; k--) {
val[k] = encoder(val[k]);
}
} else {
val = encoder(val);
}
}
}
result.push(val);
}
}
return (is_subarray) ? result : JSON.stringify(result);
}
// add the schema schema
addSchema({id: 'schema', fields: ['id', 'fields', 'meta'], meta: [0,0,0]});
// equivalent in KSON (if the schema schema were already bootstrapped)
// addSchema('["schema", ["id", "fields", "meta"], [0,0,0]]');
// exports
return global.KSON = {
addCodec: addCodec,
addSchema: addSchema,
parse: parse,
stringify: stringify,
encoders: ENCODERS,
decoders: DECODERS
};
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment