Skip to content

Commit e7a0fb2

Browse files
committed
fromJson delegation to native JSON parser if available
- native parser delegation - $xhr change to use native parser
1 parent e3ddc2b commit e7a0fb2

6 files changed

Lines changed: 77 additions & 45 deletions

File tree

perf/jsonPerfSpec.js

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,28 @@
11
describe('json', function() {
2-
xit('should parse json in a reasonable time', function() {
3-
var totalSubstr = 0,
4-
totalGetMatch = 0,
5-
totalConsume = 0,
6-
totalTime = 0,
7-
runTimes = [];
8-
9-
for (var i=0; i<10; i++) {
10-
window.substrTime = 0;
11-
window.getMatchTime = 0;
12-
window.consumeTime = 0;
13-
var start = Date.now();
14-
expect(angular.fromJson(largeJsonString)).toBeTruthy();
15-
var time = Date.now() - start;
16-
// dump('parse time', time, 'consume', window.consumeTime,
17-
// 'substr', window.substrTime,
18-
// 'getMatch', window.getMatchTime);
19-
totalTime += time;
20-
totalSubstr += window.substrTime;
21-
totalGetMatch += window.getMatchTime;
22-
totalConsume += window.consumeTime;
23-
runTimes.push(time);
24-
}
25-
26-
totalGetMatch = totalGetMatch - totalSubstr;
27-
28-
dump("totals", totalTime,
29-
"| consume", totalConsume, '' + Math.round(totalConsume/(totalTime/100)) + '%',
30-
"| substr", totalSubstr, '' + Math.round(totalSubstr/(totalTime/100)) + '%',
31-
"| getMatch", totalGetMatch, '' + Math.round(totalGetMatch/(totalTime/100)) + '%');
32-
dump("run times", runTimes);
33-
delete window.consumeTime;
34-
delete window.substrTime;
35-
delete window.getMatchTime;
36-
});
37-
382

393
it('angular parser', function() {
404
var duration = time(function() {
415
expect(angular.fromJson(largeJsonString)).toBeTruthy();
426
}, 1);
437

44-
expect(duration).toBeLessThan(4000);
8+
dump(duration/1 + ' ms per iteration');
9+
});
10+
11+
12+
it('angular delegating to native parser', function() {
13+
var duration = time(function() {
14+
expect(angular.fromJson(largeJsonString, true)).toBeTruthy();
15+
}, 100);
16+
17+
dump(duration/100 + ' ms per iteration');
4518
});
4619

4720

4821
it('native json', function() {
4922
var duration = time(function() {
5023
expect(JSON.parse(largeJsonString)).toBeTruthy();
51-
}, 1);
24+
}, 100);
5225

53-
expect(duration).toBeLessThan(200);
26+
dump(duration/100 + ' ms per iteration');
5427
});
5528
});

src/Angular.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ var _undefined = undefined,
109109
angularService = extensionMap(angular, 'service'),
110110
angularCallbacks = extensionMap(angular, 'callbacks'),
111111
nodeName,
112-
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
112+
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
113+
DATE_ISOSTRING_LN = 24;
113114

114115
/**
115116
* @workInProgress

src/JSON.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,41 @@ function toJson(obj, pretty) {
2929
* Deserializes a string in the JSON format.
3030
*
3131
* @param {string} json JSON string to deserialize.
32+
* @param {boolean} [useNative=false] Use native JSON parser if available
3233
* @returns {Object|Array|Date|string|number} Deserialized thingy.
3334
*/
34-
function fromJson(json) {
35+
function fromJson(json, useNative) {
3536
if (!json) return json;
37+
38+
var obj, p, expression;
39+
3640
try {
37-
var p = parser(json, true);
38-
var expression = p.primary();
41+
if (useNative && JSON && JSON.parse) {
42+
obj = JSON.parse(json);
43+
return transformDates(obj);
44+
}
45+
46+
p = parser(json, true);
47+
expression = p.primary();
3948
p.assertAllConsumed();
4049
return expression();
50+
4151
} catch (e) {
4252
error("fromJson error: ", json, e);
4353
throw e;
4454
}
55+
56+
// TODO make foreach optionally recursive and remove this function
57+
function transformDates(obj) {
58+
if (isString(obj) && obj.length === DATE_ISOSTRING_LN) {
59+
return angularString.toDate(obj);
60+
} else if (isArray(obj) || isObject(obj)) {
61+
foreach(obj, function(val, name) {
62+
obj[name] = transformDates(val);
63+
});
64+
}
65+
return obj;
66+
}
4567
}
4668

4769
angular['toJson'] = toJson;

src/parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var OPERATORS = {
2626
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
2727

2828
function lex(text, parseStringsForObjects){
29-
var dateParseLength = parseStringsForObjects ? 24 : -1,
29+
var dateParseLength = parseStringsForObjects ? DATE_ISOSTRING_LN : -1,
3030
tokens = [],
3131
token,
3232
index = 0,

src/services.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ angularServiceInject('$xhr', function($browser, $error, $log){
705705
$browser.xhr(method, url, post, function(code, response){
706706
try {
707707
if (isString(response) && /^\s*[\[\{]/.exec(response) && /[\}\]]\s*$/.exec(response)) {
708-
response = fromJson(response);
708+
response = fromJson(response, true);
709709
}
710710
if (code == 200) {
711711
callback(code, response);

test/JsonSpec.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,42 @@ describe('json', function(){
116116
expect(fromJson("{exp:1.2e-10}")).toEqual({exp:1.2E-10});
117117
});
118118

119+
120+
//run these tests only in browsers that have native JSON parser
121+
if (JSON && JSON.parse) {
122+
123+
describe('native parser', function() {
124+
125+
var nativeParser = JSON.parse;
126+
127+
afterEach(function() {
128+
JSON.parse = nativeParser;
129+
});
130+
131+
132+
it('should delegate to native parser if available and boolean flag is passed', function() {
133+
var spy = this.spyOn(JSON, 'parse').andCallThrough();
134+
135+
expect(fromJson('{}')).toEqual({});
136+
expect(spy).wasNotCalled();
137+
138+
expect(fromJson('{}', true)).toEqual({});
139+
expect(spy).wasCalled();
140+
});
141+
142+
143+
it('should convert timestamp strings to Date objects', function() {
144+
expect(fromJson('"2010-12-22T17:23:17.974Z"', true) instanceof Date).toBe(true);
145+
expect(fromJson('["2010-12-22T17:23:17.974Z"]', true)[0] instanceof Date).toBe(true);
146+
expect(fromJson('{"t":"2010-12-22T17:23:17.974Z"}', true).t instanceof Date).toBe(true);
147+
expect(fromJson('{"t":["2010-12-22T17:23:17.974Z"]}', true).t[0] instanceof Date).toBe(true);
148+
expect(fromJson('{"t":{"t":"2010-12-22T17:23:17.974Z"}}', true).t.t instanceof Date).toBe(true);
149+
});
150+
});
151+
152+
}
153+
154+
119155
describe('security', function(){
120156
it('should not allow naked expressions', function(){
121157
expect(function(){fromJson('1+2');}).

0 commit comments

Comments
 (0)