fromJson delegation to native JSON parser if available

- native parser delegation
- $xhr change to use native parser
This commit is contained in:
Igor Minar
2010-12-22 13:19:26 -08:00
parent e3ddc2bcc4
commit e7a0fb250f
6 changed files with 77 additions and 45 deletions

View File

@@ -1,55 +1,28 @@
describe('json', function() {
xit('should parse json in a reasonable time', function() {
var totalSubstr = 0,
totalGetMatch = 0,
totalConsume = 0,
totalTime = 0,
runTimes = [];
for (var i=0; i<10; i++) {
window.substrTime = 0;
window.getMatchTime = 0;
window.consumeTime = 0;
var start = Date.now();
expect(angular.fromJson(largeJsonString)).toBeTruthy();
var time = Date.now() - start;
// dump('parse time', time, 'consume', window.consumeTime,
// 'substr', window.substrTime,
// 'getMatch', window.getMatchTime);
totalTime += time;
totalSubstr += window.substrTime;
totalGetMatch += window.getMatchTime;
totalConsume += window.consumeTime;
runTimes.push(time);
}
totalGetMatch = totalGetMatch - totalSubstr;
dump("totals", totalTime,
"| consume", totalConsume, '' + Math.round(totalConsume/(totalTime/100)) + '%',
"| substr", totalSubstr, '' + Math.round(totalSubstr/(totalTime/100)) + '%',
"| getMatch", totalGetMatch, '' + Math.round(totalGetMatch/(totalTime/100)) + '%');
dump("run times", runTimes);
delete window.consumeTime;
delete window.substrTime;
delete window.getMatchTime;
});
it('angular parser', function() {
var duration = time(function() {
expect(angular.fromJson(largeJsonString)).toBeTruthy();
}, 1);
expect(duration).toBeLessThan(4000);
dump(duration/1 + ' ms per iteration');
});
it('angular delegating to native parser', function() {
var duration = time(function() {
expect(angular.fromJson(largeJsonString, true)).toBeTruthy();
}, 100);
dump(duration/100 + ' ms per iteration');
});
it('native json', function() {
var duration = time(function() {
expect(JSON.parse(largeJsonString)).toBeTruthy();
}, 1);
}, 100);
expect(duration).toBeLessThan(200);
dump(duration/100 + ' ms per iteration');
});
});

View File

@@ -109,7 +109,8 @@ var _undefined = undefined,
angularService = extensionMap(angular, 'service'),
angularCallbacks = extensionMap(angular, 'callbacks'),
nodeName,
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
DATE_ISOSTRING_LN = 24;
/**
* @workInProgress

View File

@@ -29,19 +29,41 @@ function toJson(obj, pretty) {
* Deserializes a string in the JSON format.
*
* @param {string} json JSON string to deserialize.
* @param {boolean} [useNative=false] Use native JSON parser if available
* @returns {Object|Array|Date|string|number} Deserialized thingy.
*/
function fromJson(json) {
function fromJson(json, useNative) {
if (!json) return json;
var obj, p, expression;
try {
var p = parser(json, true);
var expression = p.primary();
if (useNative && JSON && JSON.parse) {
obj = JSON.parse(json);
return transformDates(obj);
}
p = parser(json, true);
expression = p.primary();
p.assertAllConsumed();
return expression();
} catch (e) {
error("fromJson error: ", json, e);
throw e;
}
// TODO make foreach optionally recursive and remove this function
function transformDates(obj) {
if (isString(obj) && obj.length === DATE_ISOSTRING_LN) {
return angularString.toDate(obj);
} else if (isArray(obj) || isObject(obj)) {
foreach(obj, function(val, name) {
obj[name] = transformDates(val);
});
}
return obj;
}
}
angular['toJson'] = toJson;

View File

@@ -26,7 +26,7 @@ var OPERATORS = {
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
function lex(text, parseStringsForObjects){
var dateParseLength = parseStringsForObjects ? 24 : -1,
var dateParseLength = parseStringsForObjects ? DATE_ISOSTRING_LN : -1,
tokens = [],
token,
index = 0,

View File

@@ -705,7 +705,7 @@ angularServiceInject('$xhr', function($browser, $error, $log){
$browser.xhr(method, url, post, function(code, response){
try {
if (isString(response) && /^\s*[\[\{]/.exec(response) && /[\}\]]\s*$/.exec(response)) {
response = fromJson(response);
response = fromJson(response, true);
}
if (code == 200) {
callback(code, response);

View File

@@ -116,6 +116,42 @@ describe('json', function(){
expect(fromJson("{exp:1.2e-10}")).toEqual({exp:1.2E-10});
});
//run these tests only in browsers that have native JSON parser
if (JSON && JSON.parse) {
describe('native parser', function() {
var nativeParser = JSON.parse;
afterEach(function() {
JSON.parse = nativeParser;
});
it('should delegate to native parser if available and boolean flag is passed', function() {
var spy = this.spyOn(JSON, 'parse').andCallThrough();
expect(fromJson('{}')).toEqual({});
expect(spy).wasNotCalled();
expect(fromJson('{}', true)).toEqual({});
expect(spy).wasCalled();
});
it('should convert timestamp strings to Date objects', function() {
expect(fromJson('"2010-12-22T17:23:17.974Z"', true) instanceof Date).toBe(true);
expect(fromJson('["2010-12-22T17:23:17.974Z"]', true)[0] instanceof Date).toBe(true);
expect(fromJson('{"t":"2010-12-22T17:23:17.974Z"}', true).t instanceof Date).toBe(true);
expect(fromJson('{"t":["2010-12-22T17:23:17.974Z"]}', true).t[0] instanceof Date).toBe(true);
expect(fromJson('{"t":{"t":"2010-12-22T17:23:17.974Z"}}', true).t.t instanceof Date).toBe(true);
});
});
}
describe('security', function(){
it('should not allow naked expressions', function(){
expect(function(){fromJson('1+2');}).