2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 YUI.add('dataschema-json', function(Y) {
11 * Provides a DataSchema implementation which can be used to work with JSON data.
14 * @submodule dataschema-json
18 * JSON subclass for the DataSchema Utility.
19 * @class DataSchema.JSON
20 * @extends DataSchema.Base
27 /////////////////////////////////////////////////////////////////////////////
29 // DataSchema.JSON static methods
31 /////////////////////////////////////////////////////////////////////////////
33 * Utility function converts JSON locator strings into walkable paths
35 * @method DataSchema.JSON.getPath
36 * @param locator {String} JSON value locator.
37 * @return {String[]} Walkable path to data value.
40 getPath: function(locator) {
46 // Strip the ["string keys"] and [1] array indexes
48 replace(/\[(['"])(.*?)\1\]/g,
49 function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
51 function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
52 replace(/^\./,''); // remove leading dot
54 // Validate against problematic characters.
55 if (!/[^\w\.\$@]/.test(locator)) {
56 path = locator.split('.');
57 for (i=path.length-1; i >= 0; --i) {
58 if (path[i].charAt(0) === '@') {
59 path[i] = keys[parseInt(path[i].substr(1),10)];
70 * Utility function to walk a path and return the value located there.
72 * @method DataSchema.JSON.getLocationValue
73 * @param path {String[]} Locator path.
74 * @param data {String} Data to traverse.
75 * @return {Object} Data value at location.
78 getLocationValue: function (path, data) {
82 if(!LANG.isUndefined(data[path[i]])) {
94 * Applies a given schema to given JSON data.
97 * @param schema {Object} Schema to apply.
98 * @param data {Object} JSON data.
99 * @return {Object} Schema-parsed data.
102 apply: function(schema, data) {
104 data_out = {results:[],meta:{}};
106 // Convert incoming JSON strings
107 if(!LANG.isObject(data)) {
109 data_in = Y.JSON.parse(data);
117 if(LANG.isObject(data_in) && schema) {
118 // Parse results data
119 if(!LANG.isUndefined(schema.resultListLocator)) {
120 data_out = SchemaJSON._parseResults(schema, data_in, data_out);
124 if(!LANG.isUndefined(schema.metaFields)) {
125 data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
129 data_out.error = new Error("JSON schema parse failure");
136 * Schema-parsed list of results from full data
138 * @method _parseResults
139 * @param schema {Object} Schema to parse against.
140 * @param json_in {Object} JSON to parse.
141 * @param data_out {Object} In-progress parsed data to update.
142 * @return {Object} Parsed data object.
146 _parseResults: function(schema, json_in, data_out) {
151 if(schema.resultListLocator) {
152 path = SchemaJSON.getPath(schema.resultListLocator);
154 results = SchemaJSON.getLocationValue(path, json_in);
155 if (results === undefined) {
156 data_out.results = [];
157 error = new Error("JSON results retrieval failure");
160 if(LANG.isArray(schema.resultFields) && LANG.isArray(results)) {
161 data_out = SchemaJSON._getFieldValues(schema.resultFields, results, data_out);
164 data_out.results = [];
165 error = new Error("JSON Schema fields retrieval failure");
170 error = new Error("JSON Schema results locator failure");
174 data_out.error = error;
182 * Get field data values out of list of full results
184 * @method _getFieldValues
185 * @param fields {Array} Fields to find.
186 * @param array_in {Array} Results to parse.
187 * @param data_out {Object} In-progress parsed data to update.
188 * @return {Object} Parsed data object.
192 _getFieldValues: function(fields, array_in, data_out) {
196 field, key, path, parser,
197 simplePaths = [], complexPaths = [], fieldParsers = [],
200 // First collect hashes of simple paths, complex paths, and parsers
201 for (i=0; i<len; i++) {
202 field = fields[i]; // A field can be a simple string or a hash
203 key = field.key || field; // Find the key
205 // Validate and store locators for later
206 path = SchemaJSON.getPath(key);
208 if (path.length === 1) {
209 simplePaths[simplePaths.length] = {key:key, path:path[0]};
211 complexPaths[complexPaths.length] = {key:key, path:path};
216 // Validate and store parsers for later
217 //TODO: use Y.DataSchema.parse?
218 parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
220 fieldParsers[fieldParsers.length] = {key:key, parser:parser};
224 // Traverse list of array_in, creating records of simple fields,
225 // complex fields, and applying parsers as necessary
226 for (i=array_in.length-1; i>=0; --i) {
228 result = array_in[i];
230 // Cycle through simpleLocators
231 for (j=simplePaths.length-1; j>=0; --j) {
232 // Bug 1777850: The result might be an array instead of object
233 record[simplePaths[j].key] = Y.DataSchema.Base.parse(
234 (LANG.isUndefined(result[simplePaths[j].path]) ?
235 result[j] : result[simplePaths[j].path]), simplePaths[j]);
238 // Cycle through complexLocators
239 for (j=complexPaths.length - 1; j>=0; --j) {
240 record[complexPaths[j].key] = Y.DataSchema.Base.parse(
241 (SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
244 // Cycle through fieldParsers
245 for (j=fieldParsers.length-1; j>=0; --j) {
246 key = fieldParsers[j].key;
247 record[key] = fieldParsers[j].parser(record[key]);
249 if (LANG.isUndefined(record[key])) {
256 data_out.results = results;
261 * Parses results data according to schema
264 * @param metaFields {Object} Metafields definitions.
265 * @param json_in {Object} JSON to parse.
266 * @param data_out {Object} In-progress parsed data to update.
267 * @return {Object} Schema-parsed meta data.
271 _parseMeta: function(metaFields, json_in, data_out) {
272 if(LANG.isObject(metaFields)) {
274 for(key in metaFields) {
275 if (metaFields.hasOwnProperty(key)) {
276 path = SchemaJSON.getPath(metaFields[key]);
277 if (path && json_in) {
278 data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
284 data_out.error = new Error("JSON meta data retrieval failure");
290 Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
294 }, '3.0.0' ,{requires:['json', 'dataschema-base']});