xJson Objects

Alright everyone, I have finished the xJSON object suite, or at least a decent first pass. This was a bigger project than I expected and I apologize for dragging it out over the past couple weeks. It seemed like every time I was about to finish, I would uncover a corner case that would break an entire second of the project. Anyway, I was away over the passed weekend and had to do a lot work in the evening last week, and missed your Tuesday post as well. I hope to be better about getting two articles written a week. I am also open to guest posts, if you are interest just leave a comment on any article.

Anyway, I have written the following objects: JsonObject, JsonArray, XJsonObject, and XJsonArray. Here they are:

/**
 * The JsonObject class manages a JSON object by creating getter and setters
 * @namespace Core.Model
 * @class JsonObject
 * @dependencies library
 */
Core.Model.JsonObject = function(json) {
	this.update(json);
};

Core.extend(Core.Model.JsonObject, Core.Model.Model, {

	/**
	 * The number of values in the json object
	 * @property length
	 * @type int
	 */
	length: 0,

	/**
	 * Updates the object to use the passed data set
	 * @method update
	 * @param json {array} jsonobject object
	 * @public
	 */
	update: function(json) {
		// validation
		if (! isType(json, object)) {throw(JsonObject - Invalid data passed into Update);}
		this.parent.update.call(this, json);

		// private variables
		var data = {},
			that = this,
			n = 0;
			
		var fx = function(key, o) {
			var ckey = capitalize(key);
			data[key] = Core.Model.XJson(o);
			that[(isType(o, boolean) && -1 !== key.indexOf(is))? key: get + ckey] = function() {return data[key];};
			that[set + ckey] = function(o) {
				if ($type(o) !== $type(data[key])) {throw(JSONObject - invalid object passed into setter for:  + key);}
				data[key] = o;
			};
		};

		// iterate through the JSON object keys
		for (var key in json) {
			fx(key, json[key]);
			n += 1;
		};

		// update public variables
		this.length = n;
	}
});

/**
 * The JsonArray class manages …
 * @namespace Core.Model
 * @class JsonArray
 * @dependencies library
 */
Core.Model.JsonArray = function(json) {
	this.update(json);
};

Core.extend(Core.Model.JsonArray, Core.Model.Model, {

	/**
	 * Boolean used to inform the get method to clean the cache
	 * @property clean
	 * @type boolean
	 */
	clean: false,

	/**
	 * The number of values in the json array
	 * @property length
	 * @type int
	 */
	length: 0,
	/**
	 * Execute function fn on all elements in collection
	 * @method batch
	 * @param fn {function} the function to execute
	 * @public
	 */
	batch: function(fn) {
		Core.batch(this.data, function(o, i, scope) {
			fn(scope.get(i), i);
		}, this);
	},

	/**
	 * Retrieve the element at i from the collection; lazy definition to allow for converting objects on demand
	 * @method get
	 * @param i {int} index in the data
	 * @public
	 */
	get: function(i) {
		var that = this,
			data = [];

		this.get = function(i) {
			// commented because this is very strict and dont always want to use, sometimes returning undefined is ok
			//if (! that.data[i]) {throw(JsonArray - Invalid index passed into Get:  + i);}
			// purges cached data on an update
			if (that.clean) {
				data = [];
				that.clean = false;
			}
			
			if (! data[i] && that.data[i]) {
				data[i] = Core.Model.XJson(that.data[i]);
			}

			return data[i];
		};

		return this.get(i);
	},

	/**
	 * Inserts another value into the data structure
	 * @method push
	 * @param o {array} new row of data
	 * @public
	 */
	push: function(o) {
		if (! o) {throw(JsonArray - Invalid Object passed into Push);}
		this.data.push(o);
		this.length = this.data.length;
	},

	/**
	 * Updates the object to use the passed data set
	 * @method update
	 * @param json {array} jsonarray object
	 * @public
	 */
	update: function(json) {
		// validation
		if (! isType(json, array)) {throw(JSONArray - Invalid JSON Array Object passed into Update);}
		this.parent.update.call(this, json);
		this.clean = true;

		// update public variables
		this.length = json.length;
	}
});

/**
 * The XJsonObject class manages …
 * @namespace Core.Model
 * @class XJsonObject
 * @dependencies library
 */

Core.Model.XJsonObject = function(scheme, json) {
	if (! isType(scheme, array)) {throw(XJsonObject - Invalid scheme passed into Constructor);}
	this.scheme = scheme;
	this.update(json);
};

Core.extend(Core.Model.XJsonObject, Core.Model.Model, {

	/**
	 * The number of values in the xjson object
	 * @property length
	 * @type int
	 */
	length: 0,

	/**
	 * The object schema
	 * @property scheme
	 * @type array
	 */
	scheme: [],

	/**
	 * Updates the object to use the passed data set
	 * @method update
	 * @param json {object} XJsonObject object
	 * @public
	 */
	update: function(json) {
		// validation
		if (! isType(json, array)) {throw(XJsonObject - Invalid data passed into Update);}
		if (json.length !== this.scheme.length) {throw(XJsonObject - Invalid data (does not match scheme) passed into Update);}
		this.parent.update.call(this, json);

		// private variables
		var data = this.data,
			that = this;

		// update public variables
		this.length = data.length;

		Core.batch(this.scheme, function(key, i, json) {
			var o = json[i],
				ckey = capitalize(key);

			data[i] = Core.Model.XJson(o);

			that[(isType(o, boolean) && -1 !== key.indexOf(is))? key: get + ckey] = function() {return data[i];};
			that[set + ckey] = function(o) {
				if ($type(o) !== $type(data[key])) {throw(XJSONObject - invalid object passed into setter for:  + key);}
				data[i] = o;
			};
		}, data);
	}
});

/**
 * The XJsonArray class manages …
 * @namespace Core.Model
 * @class XJsonArray
 * @dependencies library
 */
Core.Model.XJsonArray = function(json) {
	this.update(json);
};

Core.extend(Core.Model.XJsonArray, Core.Model.Model, {

	/**
	 * Boolean used to inform the get method to clean the cache
	 * @property clean
	 * @type boolean
	 */
	clean: false,

	/**
	 * The number of values in the xjson array
	 * @property length
	 * @type int
	 */
	length: 0,

	/**
	 * The object schema
	 * @property scheme
	 * @type array
	 */
	scheme: [],

	/**
	 * Execute function fn on all elements in collection
	 * @method batch
	 * @param fn {function} the function to execute
	 * @public
	 */
	batch: function(fn) {
		Core.batch(this.data, function(o, i, scope) {
			fn(scope.get(i), i);
		}, this);
	},
	
	/**
	 * Retrieve the element at i from the collection; lazy definition to allow for converting objects on demand
	 * @method get
	 * @param i {int} index in the data
	 * @public
	 */
	get: function(i) {
		var that = this,
			data = [];

		// internally redeclaring to scope the data structure, which will contain already converted objects
		this.get = function(i) {
			// commented because this is very strict and dont always want to use, sometimes returning undefined is ok
			//if (! that.data[i]) {throw(XJsonArray - Invalid index passed into Get:  + i);}
			// purges cached data on an update
			if (that.clean) {
				data = [];
				that.clean = false;
			}
			
			if (! data[i]) {
				var o = that.data[i];

				// if array, then it is an XJsonObject
				if (isType(o, array)) {
					data[i] = new Core.Model.XJsonObject(that.scheme, o);
				}
				// otherwise, use generic method
				else {
					data[i] = Core.Model.XJson(o);
				}
			}

			return data[i];
		};

		return this.get(i);
	},

	/**
	 * Inserts another value into the data structure
	 * @method push
	 * @param o {array} new row of data
	 * @public
	 */
	push: function(o) {
		if (! (isType(o, array) && o.length === this.scheme.length)) {throw(XJsonArray - Invalid data passed into Push);}
		this.data.push(o);
		this.length = this.data.length;
	},

	/**
	 * Updates the object to use the passed data set
	 * @method update
	 * @param json {object} xjsonarray object
	 * @public
	 */
	update: function(json) {
		// validation
		if (! (json && isType(json, object) && isType(json.scheme, array) && isType(json.set, array))) {throw(XJsonArray - Invalid data passed into Update);}
		this.parent.update.call(this, json.set);
		this.clean = true;

		// update public variables
		this.length = json.length;
		this.scheme = json.scheme;
	}
});

/**
 * The XJson class manages …
 * @namespace Core.Model
 * @class XJson
 * @dependencies library
 */

Core.Model.XJson = function(o) {
	if (! o) {
		return null;
	}
	else if (isType(o, array) && (isType(o[0], array) || ! o.length)) {
		return new Core.Model.JsonArray(o);
	}
	else if (isType(o, object) && ! isType(o, array)) {
		return (o.scheme)? new Core.Model.XJsonArray(o): new Core.Model.JsonObject(o);
	}
	else {
		return o;
	}
};

This is a lot of code to discuss, which I do not have time for today (Tuesdays article will have a detailed explanation). I have introduced a String manipulation method capitalize, which converts strings like mattsnider to Mattsnider&rsquot;. This way all getter and setter methods have the appropriate capitalization. This method is from a small package of string functions, which we will discuss after these JSON objects. For now, let me know your thoughts about these objects and take a look at the test page.