actionjson
actionjson copied to clipboard
Number encoding error.
You permit NaN, Infinity and -Infinity to pass through while all other libraries (and I do mean all) filter these values to null.
Also, you parse -Infinity but throw an error on the other two values mentioned, you may want to implement better syntax filtering.
In my opinion, encoding a value as null isn't any better than letting it pass through. The other school of thought would be to throw an error, I don't think misrepresenting invalid input is a good idea.
It's important to remember I favored speed over a lot of things, including nice error messages or clear code. Never at the expense of the JSON standard, but Nan/Infinity/-Infinity aren't in it. There aren't any tests for them. Both decoders don't even attempt to decode them.
Still, being strict has its benefits. I couldn't decide if it was better to be strict (which had a speed penalty I wasn't a fan of) or permissive, so I added both. If you take a second look at both encoders you'll see an argument to enable strict number parsing. It's just turned off by default.
Also, it's hardly all JSON libraries that behave the way you describe. This is the first one I tested...
$ python
Python 2.7.3 (default, May 6 2012, 11:24:12)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> json.dumps({"x":float("NaN")})
'{"x": NaN}'
It also has a switch to turn on strict number encoding that's turned off by default.
Let's also not forget the original JSON parser...
> eval("[NaN]")
[NaN]
I'd meant all other AS3 libraries: blooddy.crypto, as3corelib, vegas/ekameleon (this one fails to parse exponent numbers), the native parser, my own. I should've been more clear about that.
I would also argue that representing NaN as null is valid, since 0+null == NaN. The Infinities are less valid as null, but because they're not in-spec, it's best to never emit them; and null is more desirable than an error in most cases. The fastest way to check for all three values in one go without a function call is to simply do:
if (v * 0 !== 0) rtn.writeInt(0x6E756C6C);
else rtn.writeUTFBytes(String(v));
Which is potentially faster since you're not allocating a String.
Since you're using the native parser in some cases now, you should probably attempt to match its output on this anyway, just so that the JSON is consistent between player versions.
The results of non-Flash JSON parsers are just as valid as the Flash ones, if not more so. Why would I base my decisions on the very libraries whose failings fueled my interest in this project? (Note: I never tested yours, I'm not sure it existed when I started this?).
I'm still not convinced that null is best, regardless of any ECMAscript shenanigans. I mean, "" == 0
, but I don't think that kind of logic has any basis in reality. Unless I can get a speed boost out of it, anyway.
That v * 0 !== 0
is a clever trick. I always used isInfinity to detect Infinity and NaN. I'll have to remember that one.
The native parser is actually turned off unless strict number parsing is turned on, so the input would either trigger an error or pass through, but not deliver different results (in this case). This can happen anyway if toJSON is overridden, so using the native parser does require some level of understanding of how it can go wrong. It's for power users, and disabled by default for that reason.
Despite all this, staying slightly more in line with the native parser does seem beneficial, even if it's on invalid input. I'll think about it.
My library probably didn't exist when you started. Though, mine is the fastest of all the options, beating out even the native parser on some real-world test data (http://www.kongregate.com/forums/4/topics/271808), mostly through trickery involving Strings (substring, indexOf) and slightly more efficient small-number parsing.
The main reason I brought it up is because those three values are invalid JSON that most parsers will reject. While I agree null isn't the best alternative, it's the closest one in the JSON spec. and there are many cases where valid input (due to strict parsers) is better than invalid, or an error.
Also, while there's an open ticket, you may want to consider special-casing Vectors to output as an Array (you can find a long-winded and relatively fast example of how to do so easily in my library; an extension of your own method); I made this test suite to compare the differences between the various libraries: http://megaswf.com/s/2443659 (Due to this site not reading compressed SWFs correctly, you need to click the "play in fullscreen" link)
I took a look at yours. Lots of interesting stuff, you live up to your reputation.
I want JSON parsers to reject NaN. I think it's a good thing. It's explicitly not in the spec, and anything not in the spec is evil. Despite this, my library prioritizes speed very highly, so by favoring speed over strictness, I can take advantage of the assumption that it will only receive valid input to reap a grand harvest of reduced CPU cycles. The strict number parsing is still there anyway, just disabled to favor speed.
Anyway, when the first two fully compatible libraries I test exhibit similar behaviors as my library, I don't think that lends anything to your opinion that it is in any way closer to the JSON spec.
I didn't add Vector support mainly because while I can encode Vectors, I would not be decoding anything as Vectors. Having a one-way operation like that doesn't jibe with my interpretation on how JSON should be used. If you have valid input, and you encode it with an encoder, decoding it should produce identical results. This kind of parity between internal and wire formats is an excellent quality for any data format.
I could still encode it in a friendlier way or throw an error, but speed trumps friendliness in actionjson, and Vector is one of the many forms of invalid data I ignore to keep things fast. Also, supporting Vectors potentially breaks compatibility with Flash 9.