circular-serializer
Version:
Safe serialize and deserialize json objects, Date support, extensible custom types
108 lines (98 loc) • 2.33 kB
JavaScript
var Traverse = require('traverse');
var SERIALIZER_LABEL = '@#csL';
var SERIALIZER_VALUE = '@#csV';
var defaultTypeMap = {
Date: {
detect: function (x) {
return x instanceof Date;
},
pack: function(x) {
return x.toJSON();
},
unpack: function(x) {
return new Date(x);
}
},
undef: {
detect: function (x) {
return x === undefined;
},
pack: function(x) {
return '.';
},
unpack: function(x) {
return undefined;
}
},
nan: {
detect: function (x) {
return typeof x === 'number' && isNaN(x);
},
pack: function(x) {
return '.';
},
unpack: function(x) {
return NaN;
}
}
};
function serializable(obj) {
return !(obj === undefined || obj === true || obj === false || obj === null || typeof obj == 'number');
}
function Serializer(options) {
if (!(this instanceof Serializer)) {
return new Serializer(options);
}
options = options || {};
this._typeMap = options.typeMap || Serializer.defaultTypeMap;
}
Serializer.defaultTypeMap = defaultTypeMap;
var proto = Serializer.prototype;
proto.deserialize = function deserialize(string) {
var typeMap = this._typeMap;
function deserializeMap(x) {
if (
typeof x === 'object' &&
x !== null &&
x.hasOwnProperty(SERIALIZER_LABEL) &&
x.hasOwnProperty(SERIALIZER_VALUE)
) {
this.update(typeMap[x[SERIALIZER_LABEL]].unpack(x[SERIALIZER_VALUE]));
}
}
return serializable(string) ? Traverse(JSON.parse(string)).map(deserializeMap) : string;
};
proto.serialize = function serialize(obj) {
return serializable(obj) ? JSON.stringify(this._cleanObject(obj)) : obj;
};
proto._cleanObject = function _cleanObject(obj) {
var data;
if (Array.isArray(obj)) {
data = obj.map(this._cleanObject);
} else if (typeof obj === 'object') {
var typeMap = this._typeMap;
function cleanObjectMap(x) {
if (typeof x === 'function') {
this.update('[Function]');
}
if (this.circular) {
this.update('[Circular#' + this.path + ']');
}
for(var label in typeMap) {
var type = typeMap[label];
if (type.detect(x)) {
var data = {};
data[SERIALIZER_VALUE] = type.pack(x);
data[SERIALIZER_LABEL] = label;
this.update(data);
break;
}
}
}
data = Traverse(obj).map(cleanObjectMap);
} else {
data = obj;
}
return data;
};
module.exports = Serializer;