diff options
| -rw-r--r-- | perf/jsonPerfSpec.js | 51 | ||||
| -rw-r--r-- | src/Angular.js | 3 | ||||
| -rw-r--r-- | src/JSON.js | 28 | ||||
| -rw-r--r-- | src/parser.js | 2 | ||||
| -rw-r--r-- | src/services.js | 2 | ||||
| -rw-r--r-- | test/JsonSpec.js | 36 |
6 files changed, 77 insertions, 45 deletions
diff --git a/perf/jsonPerfSpec.js b/perf/jsonPerfSpec.js index c232aed8..01a489e2 100644 --- a/perf/jsonPerfSpec.js +++ b/perf/jsonPerfSpec.js @@ -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'); }); }); diff --git a/src/Angular.js b/src/Angular.js index ea5d1ea1..991598e1 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -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 diff --git a/src/JSON.js b/src/JSON.js index 2906362c..399b2197 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -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; diff --git a/src/parser.js b/src/parser.js index a9b66c52..d233b15f 100644 --- a/src/parser.js +++ b/src/parser.js @@ -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, diff --git a/src/services.js b/src/services.js index d49b5370..36c43564 100644 --- a/src/services.js +++ b/src/services.js @@ -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); diff --git a/test/JsonSpec.js b/test/JsonSpec.js index ba3366e5..6d8a40e4 100644 --- a/test/JsonSpec.js +++ b/test/JsonSpec.js @@ -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');}). |
