2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.com/yui/license.html
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)];
64
Y.log("Invalid locator: " + locator, "error", "dataschema-json");
71
* Utility function to walk a path and return the value located there.
73
* @method DataSchema.JSON.getLocationValue
74
* @param path {String[]} Locator path.
75
* @param data {String} Data to traverse.
76
* @return {Object} Data value at location.
79
getLocationValue: function (path, data) {
84
LANG.isObject(data) &&
98
* Applies a given schema to given JSON data.
101
* @param schema {Object} Schema to apply.
102
* @param data {Object} JSON data.
103
* @return {Object} Schema-parsed data.
106
apply: function(schema, data) {
108
data_out = {results:[],meta:{}};
110
// Convert incoming JSON strings
111
if(!LANG.isObject(data)) {
113
data_in = Y.JSON.parse(data);
121
if(LANG.isObject(data_in) && schema) {
122
// Parse results data
123
if(!LANG.isUndefined(schema.resultListLocator)) {
124
data_out = SchemaJSON._parseResults.call(this, schema, data_in, data_out);
128
if(!LANG.isUndefined(schema.metaFields)) {
129
data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
133
Y.log("JSON data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-json");
134
data_out.error = new Error("JSON schema parse failure");
141
* Schema-parsed list of results from full data
143
* @method _parseResults
144
* @param schema {Object} Schema to parse against.
145
* @param json_in {Object} JSON to parse.
146
* @param data_out {Object} In-progress parsed data to update.
147
* @return {Object} Parsed data object.
151
_parseResults: function(schema, json_in, data_out) {
156
if(schema.resultListLocator) {
157
path = SchemaJSON.getPath(schema.resultListLocator);
159
results = SchemaJSON.getLocationValue(path, json_in);
160
if (results === undefined) {
161
data_out.results = [];
162
error = new Error("JSON results retrieval failure");
165
if(LANG.isArray(results)) {
166
// if no result fields are passed in, then just take the results array whole-hog
167
// Sometimes you're getting an array of strings, or want the whole object,
168
// so resultFields don't make sense.
169
if (LANG.isArray(schema.resultFields)) {
170
data_out = SchemaJSON._getFieldValues.call(this, schema.resultFields, results, data_out);
173
data_out.results = results;
177
data_out.results = [];
178
error = new Error("JSON Schema fields retrieval failure");
183
error = new Error("JSON Schema results locator failure");
187
Y.log("JSON data could not be parsed: " + Y.dump(json_in), "error", "dataschema-json");
188
data_out.error = error;
196
* Get field data values out of list of full results
198
* @method _getFieldValues
199
* @param fields {Array} Fields to find.
200
* @param array_in {Array} Results to parse.
201
* @param data_out {Object} In-progress parsed data to update.
202
* @return {Object} Parsed data object.
206
_getFieldValues: function(fields, array_in, data_out) {
210
field, key, path, parser,
211
simplePaths = [], complexPaths = [], fieldParsers = [],
214
// First collect hashes of simple paths, complex paths, and parsers
215
for (i=0; i<len; i++) {
216
field = fields[i]; // A field can be a simple string or a hash
217
key = field.key || field; // Find the key
219
// Validate and store locators for later
220
path = SchemaJSON.getPath(key);
222
if (path.length === 1) {
223
simplePaths[simplePaths.length] = {key:key, path:path[0]};
225
complexPaths[complexPaths.length] = {key:key, path:path};
228
Y.log("Invalid key syntax: " + key, "warn", "dataschema-json");
231
// Validate and store parsers for later
232
//TODO: use Y.DataSchema.parse?
233
parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
235
fieldParsers[fieldParsers.length] = {key:key, parser:parser};
239
// Traverse list of array_in, creating records of simple fields,
240
// complex fields, and applying parsers as necessary
241
for (i=array_in.length-1; i>=0; --i) {
243
result = array_in[i];
245
// Cycle through simpleLocators
246
for (j=simplePaths.length-1; j>=0; --j) {
247
// Bug 1777850: The result might be an array instead of object
248
record[simplePaths[j].key] = Y.DataSchema.Base.parse.call(this,
249
(LANG.isUndefined(result[simplePaths[j].path]) ?
250
result[j] : result[simplePaths[j].path]), simplePaths[j]);
253
// Cycle through complexLocators
254
for (j=complexPaths.length - 1; j>=0; --j) {
255
record[complexPaths[j].key] = Y.DataSchema.Base.parse.call(this,
256
(SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
259
// Cycle through fieldParsers
260
for (j=fieldParsers.length-1; j>=0; --j) {
261
key = fieldParsers[j].key;
262
record[key] = fieldParsers[j].parser.call(this, record[key]);
264
if (LANG.isUndefined(record[key])) {
271
data_out.results = results;
276
* Parses results data according to schema
279
* @param metaFields {Object} Metafields definitions.
280
* @param json_in {Object} JSON to parse.
281
* @param data_out {Object} In-progress parsed data to update.
282
* @return {Object} Schema-parsed meta data.
286
_parseMeta: function(metaFields, json_in, data_out) {
287
if(LANG.isObject(metaFields)) {
289
for(key in metaFields) {
290
if (metaFields.hasOwnProperty(key)) {
291
path = SchemaJSON.getPath(metaFields[key]);
292
if (path && json_in) {
293
data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
299
data_out.error = new Error("JSON meta data retrieval failure");
305
Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
309
}, '3.2.0' ,{requires:['json', 'dataschema-base']});