My desire is a "safe" way to encode a given object to JSON. This is for a logging any abstract object in Node.js but can apply to other needs as well. My research pointed me to util.inspect in Node, but this isn't valid JSON, and my desire is to have valid JSON that I can re-hydrate later. Because some Error instances can have recursion issues, I needed to limit the response. Also, in some cases forward and back-references to objects can be an issue as well, so I want to avoid duplicate entries.
2 に答える
必要なものであるstringifyメソッドを備えたネイティブのjavascriptJSONオブジェクトがあります:http ://www.javascripture.com/JSON#stringify
NEW ANSWER
Today, in case anyone is looking at this, I would just use fclone which at this point is much more thorough than my original.
Original Answer:
I'm answering my own question below, as this was a bit difficult to come up with a working solution. It's the combination of a few solutions I've found, and seems to work well.
/* lib/clone-safe.js
* provides a method for creating a clone of an object without redundant object references, and protected from recursion.
* The cloned value *should* always be safe for JSON.stringify(cloneSafe(myobj))
**/
(function(){
var undef; //intentionally undefined value
function cloneSafe(obj,depth){
var refs = []; //reference to cloned objects
depth = +depth > 0 && +depth || 6; //max recursion level
var layerNumber = 0; //current layer being checked
var ret = clone(obj); //start cloning
//cleanup reference checks
while(refs.length) {
delete (refs.shift()).___copied;
}
//return the result
return ret;
//recursive clone method
function clone(obj) {
if (typeof obj == "function") return undef; //no function replication
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
//max recursion reached
if (++layerNumber >= depth) {
layerNumber--;
return undef;
}
//handle circular and duplicate references
if (obj.___copied) return undef; //already included
obj.___copied = true;
refs.push(obj);
var copy = {};
//export prototype
var m = obj.constructor && obj.constructor.toString().match(/function\s+([^\(]+)/);
if (m && m[1]) copy._prototype = m[1];
//get expected properties from any error
if (obj instanceof Error) {
copy.message = obj.message || "Error";
if (obj.stack) copy.stack = obj.stack;
if (obj.number) copy.number = obj.number;
if (obj.description) copy.description = obj.description;
if (obj.name) copy.name = obj.name;
}
for (var attr in obj) {
if (attr == "___copied") continue;
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
if (obj.prototype) {
for (var attr in obj.prototype) {
if (obj.prototype.hasOwnProperty(attr) && typeof obj.prototype[attr] !== 'function') copy[attr] = clone(obj.prototype[attr]);
delete obj.prototype[attr].___copied; //allow prototypes to be re-scanned
}
}
layerNumber--;
return copy;
}
//throw new Error("Unable to copy obj! Its type isn't supported.");
console.log("Unable to copy obj! Unsupported type: %s", typeof obj);
return undef; //unable to clone the object in question
}
}
// AMD / RequireJS
if (typeof define === "function" && define.amd) {
define("clone-safe",[],function(){ return cloneSafe; });
}
// Node.js / CommonJS
else if (typeof module !== "undefined" && module.exports) {
module.exports = cloneSafe;
}
// browser/script include
else {
//provide a method for reverting global binding
var root = this; //global on the server, window in the browser.
var previousCloneSafe = root.cloneSafe; //backreference
cloneSafe.noConflict = function(){
root.cloneSafe = previousCloneSafe;
return cloneSafe;
};
//bind to the global object
root.cloneSafe = cloneSafe;
}
}());
There is some extra logic for dealing with objects that are errors, and making sure to get necessary data from said errors, and from a given object's prototype. functions are expressly ignored in the response. When recursion goes too deep, it will return undefined for a given object.