dataparser-json.js revision 6608a5406cae6b74c06f0a501e189d1fc173dc21
0N/A/**
3909N/A * The DataParser utility provides a common configurable interface for widgets to
0N/A * parse a variety of data against a given schema.
0N/A *
0N/A * @module dataparser
0N/A */
2362N/Avar LANG = Y.Lang,
0N/A
2362N/A/**
0N/A * JSON subclass for the YUI DataParser utility.
0N/A * @class DataParser.JSON
0N/A * @extends DataParser.Base
0N/A * @static
0N/A */
0N/ADPJSON = {
0N/A
0N/A /////////////////////////////////////////////////////////////////////////////
0N/A //
0N/A // DataParser.JSON static methods
0N/A //
2362N/A /////////////////////////////////////////////////////////////////////////////
2362N/A
2362N/A /**
0N/A * Utility function converts JSON locator strings into walkable paths
0N/A *
0N/A * @method DataParser.JSON.buildPath
0N/A * @param locator {String} JSON value locator.
0N/A * @return {String[]} Walkable path to data value.
0N/A * @static
0N/A */
0N/A buildPath: function(locator) {
0N/A var path = null,
0N/A keys = [],
0N/A i = 0;
0N/A
0N/A if (locator) {
0N/A // Strip the ["string keys"] and [1] array indexes
0N/A locator = locator.
0N/A replace(/\[(['"])(.*?)\1\]/g,
0N/A function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
0N/A replace(/\[(\d+)\]/g,
0N/A function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
0N/A replace(/^\./,''); // remove leading dot
0N/A
0N/A // Validate against problematic characters.
0N/A if (!/[^\w\.\$@]/.test(locator)) {
0N/A path = locator.split('.');
0N/A for (i=path.length-1; i >= 0; --i) {
0N/A if (path[i].charAt(0) === '@') {
1676N/A path[i] = keys[parseInt(path[i].substr(1),10)];
1676N/A }
0N/A }
0N/A }
0N/A else {
0N/A Y.log("Invalid locator: " + locator, "error", this.toString());
0N/A }
0N/A }
0N/A return path;
0N/A },
0N/A
0N/A /**
0N/A * Utility function to walk a path and return the value located there.
0N/A *
0N/A * @method DataParser.JSON.walkPath
0N/A * @param path {String[]} Locator path.
0N/A * @param data {String} Data to traverse.
0N/A * @return {Object} Data value at location.
0N/A * @static
0N/A */
0N/A walkPath: function (path, data) {
0N/A var i = 0,
0N/A len = path.length;
0N/A for (;i<len;i++) {
0N/A data = data[path[i]];
0N/A }
0N/A return data;
0N/A },
0N/A
0N/A /**
0N/A * Overriding parse method traverses JSON data according to given schema.
0N/A *
0N/A * @method _parse
0N/A * @param schema {Object} Schema to parse against.
0N/A * @param data {Object} Data to parse.
0N/A * @return {Object} Schema-parsed data.
0N/A * @static
0N/A * @protected
0N/A */
0N/A _parse: function(schema, data) {
0N/A var data_in = (data.responseText && Y.JSON.parse(data.responseText)) || data,
0N/A data_out = {results:[],meta:{}};
0N/A
0N/A if(LANG.isObject(data_in) && schema) {
0N/A // Parse results data
0N/A data_out = this._parseResults(schema, data_in, data_out);
0N/A
0N/A // Parse meta data
0N/A if(LANG.isObject(schema.metaFields)) {
0N/A data_out = this._parseMeta(schema.metaFields, data_in, data_out);
0N/A }
0N/A }
0N/A else {
0N/A Y.log("JSON data could not be parsed: " + Y.dump(data_in), "error", this.toString());
0N/A data_out.error = true;
0N/A }
0N/A
0N/A return data_out;
0N/A },
0N/A
0N/A /**
0N/A * Schema-parse list of results from full data
0N/A *
0N/A * @method _parseResults
0N/A * @return {Array} Array of results.
0N/A * @static
0N/A * @protected
0N/A */
0N/A _parseResults: function(schema, data_in, data_out) {
0N/A if(schema.resultsList) {
0N/A var bError = false,
0N/A results = [],
0N/A path;
0N/A
0N/A path = DPJSON.buildPath(schema.resultsList);
0N/A if(path) {
0N/A results = DPJSON.walkPath(path, data_in);
0N/A if (results === undefined) {
0N/A bError = true;
0N/A }
0N/A else {
0N/A if(LANG.isArray(schema.fields)) {
0N/A if(LANG.isArray(schema.fields)) {
0N/A results = this._filterFieldValues(schema.fields, results);
0N/A }
0N/A else {
0N/A bError = true;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A else {
0N/A bError = true;
0N/A }
0N/A
0N/A if (bError) {
0N/A Y.log("JSON data could not be parsed: " + Y.dump(data_in), "error", this.toString());
0N/A data_out.error = true;
0N/A }
0N/A
0N/A data_out.results = results;
0N/A }
0N/A return data_out;
0N/A },
1676N/A
1676N/A /**
1676N/A * Schema-parse field data out of list of full results
1676N/A *
1676N/A * @method _filterFieldValues
1676N/A * @return {Array} Array of field-filtered results.
1676N/A * @static
1676N/A * @protected
1676N/A */
1676N/A _filterFieldValues: function(fields, results) {
1676N/A var data_out = [],
1676N/A len = fields.length,
1676N/A i, j,
1676N/A field, key, path, parser,
1676N/A simplePaths = [], complexPaths = [], fieldParsers = [],
1676N/A result, record;
1676N/A
0N/A // First collect hashes of simple paths, complex paths, and parsers
0N/A for (i=0; i<len; i++) {
1676N/A field = fields[i]; // A field can be a simple string or a hash
1676N/A key = field.key || field; // Find the key
1676N/A
1676N/A // Validate and store locators for later
1676N/A path = DPJSON.buildPath(key);
1676N/A if (path) {
1676N/A if (path.length === 1) {
1676N/A simplePaths[simplePaths.length] = {key:key, path:path[0]};
1676N/A } else {
1676N/A complexPaths[complexPaths.length] = {key:key, path:path};
1676N/A }
0N/A } else {
1676N/A Y.log("Invalid key syntax: " + key, "warn", this.toString());
1676N/A }
1676N/A
3480N/A // Validate and store parsers for later
3480N/A parser = (LANG.isFunction(field.parser)) ? field.parser : Y.DataParser[field.parser+''];
3480N/A if (parser) {
1676N/A fieldParsers[fieldParsers.length] = {key:key, parser:parser};
1676N/A }
0N/A }
1676N/A
1676N/A // Traverse list of results, creating records of simple fields,
1676N/A // complex fields, and applying parsers as necessary
0N/A for (i=results.length-1; i>=0; --i) {
0N/A record = {};
0N/A result = results[i];
0N/A if(result) {
0N/A // Cycle through simpleLocators
0N/A for (j=simplePaths.length-1; j>=0; --j) {
0N/A // Bug 1777850: The result might be an array instead of object
0N/A record[simplePaths[j].key] =
0N/A LANG.isUndefined(result[simplePaths[j].path]) ?
0N/A result[j] : result[simplePaths[j].path];
0N/A }
0N/A
0N/A // Cycle through complexLocators
0N/A for (j=complexPaths.length - 1; j>=0; --j) {
0N/A record[complexPaths[j].key] = DPJSON.walkPath(complexPaths[j].path, result);
0N/A }
0N/A
0N/A // Cycle through fieldParsers
0N/A for (j=fieldParsers.length-1; j>=0; --j) {
0N/A key = fieldParsers[j].key;
0N/A record[key] = fieldParsers[j].parser(record[key]);
0N/A // Safety net
0N/A if (LANG.isUndefined(record[key])) {
0N/A record[key] = null;
0N/A }
0N/A }
0N/A }
0N/A data_out[i] = record;
0N/A }
0N/A
0N/A return data_out;
0N/A },
0N/A
0N/A /**
0N/A * Parses results data according to schema
0N/A *
0N/A * @method _parseMeta
0N/A * @return {Object} Schema-parsed meta data.
0N/A * @static
0N/A * @protected
0N/A */
_parseMeta: function(metaFields, data_in, data_out) {
var key, path;
for(key in metaFields) {
if (metaFields.hasOwnProperty(key)) {
path = DPJSON.buildPath(metaFields[key]);
if (path && data_in) {
data_out.meta[key] = DPJSON.walkPath(path, data_in);
}
}
}
return data_out;
}
};
Y.mix(DPJSON, Y.DataParser.Base);
Y.DataParser.JSON = DPJSON;