|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). Published under the Clear BSD license. |
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|
* full text of the license. */ |
|
|
|
/** |
|
* Note: |
|
* This work draws heavily from the public domain JSON serializer/deserializer |
|
* at http://www.json.org/json.js. Rewritten so that it doesn't modify |
|
* basic data prototypes. |
|
*/ |
|
|
|
/** |
|
* @requires OpenLayers/Format.js |
|
*/ |
|
|
|
/** |
|
* Class: OpenLayers.Format.JSON |
|
* A parser to read/write JSON safely. Create a new instance with the |
|
* <OpenLayers.Format.JSON> constructor. |
|
* |
|
* Inherits from: |
|
* - <OpenLayers.Format> |
|
*/ |
|
OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, { |
|
|
|
/** |
|
* APIProperty: indent |
|
* {String} For "pretty" printing, the indent string will be used once for |
|
* each indentation level. |
|
*/ |
|
indent: " ", |
|
|
|
/** |
|
* APIProperty: space |
|
* {String} For "pretty" printing, the space string will be used after |
|
* the ":" separating a name/value pair. |
|
*/ |
|
space: " ", |
|
|
|
/** |
|
* APIProperty: newline |
|
* {String} For "pretty" printing, the newline string will be used at the |
|
* end of each name/value pair or array item. |
|
*/ |
|
newline: "\n", |
|
|
|
/** |
|
* Property: level |
|
* {Integer} For "pretty" printing, this is incremented/decremented during |
|
* serialization. |
|
*/ |
|
level: 0, |
|
|
|
/** |
|
* Property: pretty |
|
* {Boolean} Serialize with extra whitespace for structure. This is set |
|
* by the <write> method. |
|
*/ |
|
pretty: false, |
|
|
|
/** |
|
* Constructor: OpenLayers.Format.JSON |
|
* Create a new parser for JSON. |
|
* |
|
* Parameters: |
|
* options - {Object} An optional object whose properties will be set on |
|
* this instance. |
|
*/ |
|
initialize: function(options) { |
|
OpenLayers.Format.prototype.initialize.apply(this, [options]); |
|
}, |
|
|
|
/** |
|
* APIMethod: read |
|
* Deserialize a json string. |
|
* |
|
* Parameters: |
|
* json - {String} A JSON string |
|
* filter - {Function} A function which will be called for every key and |
|
* value at every level of the final result. Each value will be |
|
* replaced by the result of the filter function. This can be used to |
|
* reform generic objects into instances of classes, or to transform |
|
* date strings into Date objects. |
|
* |
|
* Returns: |
|
* {Object} An object, array, string, or number . |
|
*/ |
|
read: function(json, filter) { |
|
/** |
|
* Parsing happens in three stages. In the first stage, we run the text |
|
* against a regular expression which looks for non-JSON |
|
* characters. We are especially concerned with '()' and 'new' |
|
* because they can cause invocation, and '=' because it can cause |
|
* mutation. But just to be safe, we will reject all unexpected |
|
* characters. |
|
*/ |
|
try { |
|
if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@'). |
|
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). |
|
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
|
|
|
/** |
|
* In the second stage we use the eval function to compile the |
|
* text into a JavaScript structure. The '{' operator is |
|
* subject to a syntactic ambiguity in JavaScript - it can |
|
* begin a block or an object literal. We wrap the text in |
|
* parens to eliminate the ambiguity. |
|
*/ |
|
var object = eval('(' + json + ')'); |
|
|
|
/** |
|
* In the optional third stage, we recursively walk the new |
|
* structure, passing each name/value pair to a filter |
|
* function for possible transformation. |
|
*/ |
|
if(typeof filter === 'function') { |
|
function walk(k, v) { |
|
if(v && typeof v === 'object') { |
|
for(var i in v) { |
|
if(v.hasOwnProperty(i)) { |
|
v[i] = walk(i, v[i]); |
|
} |
|
} |
|
} |
|
return filter(k, v); |
|
} |
|
object = walk('', object); |
|
} |
|
|
|
if(this.keepData) { |
|
this.data = object; |
|
} |
|
|
|
return object; |
|
} |
|
} catch(e) { |
|
// Fall through if the regexp test fails. |
|
} |
|
return null; |
|
}, |
|
|
|
/** |
|
* APIMethod: write |
|
* Serialize an object into a JSON string. |
|
* |
|
* Parameters: |
|
* value - {String} The object, array, string, number, boolean or date |
|
* to be serialized. |
|
* pretty - {Boolean} Structure the output with newlines and indentation. |
|
* Default is false. |
|
* |
|
* Returns: |
|
* {String} The JSON string representation of the input value. |
|
*/ |
|
write: function(value, pretty) { |
|
this.pretty = !!pretty; |
|
var json = null; |
|
var type = typeof value; |
|
if(this.serialize[type]) { |
|
try { |
|
json = this.serialize[type].apply(this, [value]); |
|
} catch(err) { |
|
OpenLayers.Console.error("Trouble serializing: " + err); |
|
} |
|
} |
|
return json; |
|
}, |
|
|
|
/** |
|
* Method: writeIndent |
|
* Output an indentation string depending on the indentation level. |
|
* |
|
* Returns: |
|
* {String} An appropriate indentation string. |
|
*/ |
|
writeIndent: function() { |
|
var pieces = []; |
|
if(this.pretty) { |
|
for(var i=0; i<this.level; ++i) { |
|
pieces.push(this.indent); |
|
} |
|
} |
|
return pieces.join(''); |
|
}, |
|
|
|
/** |
|
* Method: writeNewline |
|
* Output a string representing a newline if in pretty printing mode. |
|
* |
|
* Returns: |
|
* {String} A string representing a new line. |
|
*/ |
|
writeNewline: function() { |
|
return (this.pretty) ? this.newline : ''; |
|
}, |
|
|
|
/** |
|
* Method: writeSpace |
|
* Output a string representing a space if in pretty printing mode. |
|
* |
|
* Returns: |
|
* {String} A space. |
|
*/ |
|
writeSpace: function() { |
|
return (this.pretty) ? this.space : ''; |
|
}, |
|
|
|
/** |
|
* Property: serialize |
|
* Object with properties corresponding to the serializable data types. |
|
* Property values are functions that do the actual serializing. |
|
*/ |
|
serialize: { |
|
/** |
|
* Method: serialize.object |
|
* Transform an object into a JSON string. |
|
* |
|
* Parameters: |
|
* object - {Object} The object to be serialized. |
|
* |
|
* Returns: |
|
* {String} A JSON string representing the object. |
|
*/ |
|
'object': function(object) { |
|
// three special objects that we want to treat differently |
|
if(object == null) { |
|
return "null"; |
|
} |
|
if(object.constructor == Date) { |
|
return this.serialize.date.apply(this, [object]); |
|
} |
|
if(object.constructor == Array) { |
|
return this.serialize.array.apply(this, [object]); |
|
} |
|
var pieces = ['{']; |
|
this.level += 1; |
|
var key, keyJSON, valueJSON; |
|
|
|
var addComma = false; |
|
for(key in object) { |
|
if(object.hasOwnProperty(key)) { |
|
// recursive calls need to allow for sub-classing |
|
keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, |
|
[key, this.pretty]); |
|
valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, |
|
[object[key], this.pretty]); |
|
if(keyJSON != null && valueJSON != null) { |
|
if(addComma) { |
|
pieces.push(','); |
|
} |
|
pieces.push(this.writeNewline(), this.writeIndent(), |
|
keyJSON, ':', this.writeSpace(), valueJSON); |
|
addComma = true; |
|
} |
|
} |
|
} |
|
|
|
this.level -= 1; |
|
pieces.push(this.writeNewline(), this.writeIndent(), '}'); |
|
return pieces.join(''); |
|
}, |
|
|
|
/** |
|
* Method: serialize.array |
|
* Transform an array into a JSON string. |
|
* |
|
* Parameters: |
|
* array - {Array} The array to be serialized |
|
* |
|
* Returns: |
|
* {String} A JSON string representing the array. |
|
*/ |
|
'array': function(array) { |
|
var json; |
|
var pieces = ['[']; |
|
this.level += 1; |
|
|
|
for(var i=0, len=array.length; i<len; ++i) { |
|
// recursive calls need to allow for sub-classing |
|
json = OpenLayers.Format.JSON.prototype.write.apply(this, |
|
[array[i], this.pretty]); |
|
if(json != null) { |
|
if(i > 0) { |
|
pieces.push(','); |
|
} |
|
pieces.push(this.writeNewline(), this.writeIndent(), json); |
|
} |
|
} |
|
|
|
this.level -= 1; |
|
pieces.push(this.writeNewline(), this.writeIndent(), ']'); |
|
return pieces.join(''); |
|
}, |
|
|
|
/** |
|
* Method: serialize.string |
|
* Transform a string into a JSON string. |
|
* |
|
* Parameters: |
|
* string - {String} The string to be serialized |
|
* |
|
* Returns: |
|
* {String} A JSON string representing the string. |
|
*/ |
|
'string': function(string) { |
|
// If the string contains no control characters, no quote characters, and no |
|
// backslash characters, then we can simply slap some quotes around it. |
|
// Otherwise we must also replace the offending characters with safe |
|
// sequences. |
|
var m = { |
|
'\b': '\\b', |
|
'\t': '\\t', |
|
'\n': '\\n', |
|
'\f': '\\f', |
|
'\r': '\\r', |
|
'"' : '\\"', |
|
'\\': '\\\\' |
|
}; |
|
if(/["\\\x00-\x1f]/.test(string)) { |
|
return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) { |
|
var c = m[b]; |
|
if(c) { |
|
return c; |
|
} |
|
c = b.charCodeAt(); |
|
return '\\u00' + |
|
Math.floor(c / 16).toString(16) + |
|
(c % 16).toString(16); |
|
}) + '"'; |
|
} |
|
return '"' + string + '"'; |
|
}, |
|
|
|
/** |
|
* Method: serialize.number |
|
* Transform a number into a JSON string. |
|
* |
|
* Parameters: |
|
* number - {Number} The number to be serialized. |
|
* |
|
* Returns: |
|
* {String} A JSON string representing the number. |
|
*/ |
|
'number': function(number) { |
|
return isFinite(number) ? String(number) : "null"; |
|
}, |
|
|
|
/** |
|
* Method: serialize.boolean |
|
* Transform a boolean into a JSON string. |
|
* |
|
* Parameters: |
|
* bool - {Boolean} The boolean to be serialized. |
|
* |
|
* Returns: |
|
* {String} A JSON string representing the boolean. |
|
*/ |
|
'boolean': function(bool) { |
|
return String(bool); |
|
}, |
|
|
|
/** |
|
* Method: serialize.object |
|
* Transform a date into a JSON string. |
|
* |
|
* Parameters: |
|
* date - {Date} The date to be serialized. |
|
* |
|
* Returns: |
|
* {String} A JSON string representing the date. |
|
*/ |
|
'date': function(date) { |