Based on your comments, it sounds like you don't want to know whether a string is valid JSON, but rather whether an object could be successfully encoded as JSON (e.g. doesn't contain any Date objects, instances of user-defined classes, etc.).
There are two approaches here: try to analyze the object and its "children" (watch out for recursive objects) or suck-it-and-see. If you have a JSON encoder on hand (JSON.stringify
in recent browsers or a plugin such as jquery-json), the latter is probably the simpler and more robust approach:
function canJSON(value) {
try {
JSON.stringify(value);
return true;
} catch (ex) {
return false;
}
}
Analyzing an object directly requires that you be able to tell whether it is a "plain" object (i.e. created using an object literal or new Object()
), which in turn requires you be able to get its prototype, which isn't always straightforward. I've found the following code to work in IE7, FF3, Opera 10, Safari 4, and Chrome (and quite likely other versions of those browsers, which I simply haven't tested).
var getPrototypeOf;
if (Object.getPrototypeOf) {
getPrototypeOf = Object.getPrototypeOf;
} else if (typeof ({}).__proto__ === "object") {
getPrototypeOf = function(object) {
return object.__proto__;
}
} else {
getPrototypeOf = function(object) {
var constructor = object.constructor;
if (Object.prototype.hasOwnProperty.call(object, "constructor")) {
var oldConstructor = constructor; // save modified value
if (!(delete object.constructor)) { // attempt to "unmask" real constructor
return null; // no mask
}
constructor = object.constructor; // obtain reference to real constructor
object.constructor = oldConstructor; // restore modified value
}
return constructor ? constructor.prototype : null;
}
}
// jQuery.isPlainObject() returns false in IE for (new Object())
function isPlainObject(value) {
if (typeof value !== "object" || value === null) {
return false;
}
var proto = getPrototypeOf(value);
// the prototype of simple objects is an object whose prototype is null
return proto !== null && getPrototypeOf(proto) === null;
}
var serializablePrimitives = { "boolean" : true, "number" : true, "string" : true }
function isSerializable(value) {
if (serializablePrimitives[typeof value] || value === null) {
return true;
}
if (value instanceof Array) {
var length = value.length;
for (var i = 0; i < length; i++) {
if (!isSerializable(value[i])) {
return false;
}
}
return true;
}
if (isPlainObject(value)) {
for (var key in value) {
if (!isSerializable(value[key])) {
return false;
}
}
return true;
}
return false;
}
So yeah… I'd recommend the try/catch approach. ;-)