racket icon indicating copy to clipboard operation
racket copied to clipboard

Added +inf.0 and -inf.0 support to json

Open wluker opened this issue 8 years ago • 3 comments

I don't know that inf is part of the json standard, but this allows us to pass +inf.0 and -inf.0 values and seems to work in practice. (string->jsexpr (jsexpr->string +inf.0))

wluker avatar Jan 25 '18 18:01 wluker

I don't like this technique of overflowing the parser.

I think it would be better to encode +inf and -inf as the JavaScript "properties" Infinity and -Infinity. Python does this:

$ python3.5
Python 3.5.1 (default, Apr 18 2016, 11:46:32) 
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> json.dumps(float('inf'))
'Infinity'
>>> json.dumps(float('-inf'))
'-Infinity'
>>> json.loads('Infinity')
inf
>>> json.loads('-Infinity')
-inf

But I think it would be even better to not support +inf and -inf. According to the JSON RFC4627, Section 2.4:

Numeric values that cannot be represented as sequences of digits (such as Infinity and NaN) are not permitted.

The Ruby 2.0 'json' library seems to implement this:

$ ruby --version
ruby 2.0.0p645 (2015-04-13 revision 50299) [universal.x86_64-darwin15]
$ irb --version                                                                                                                                                     
irb 0.9.6(09/06/30)
$irb
irb(main):001:0> require 'json'
=> true
irb(main):002:0> JSON.parse('Infinity')
JSON::ParserError: 757: unexpected token at 'Infinity'
	from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:155:in `parse'
	from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:155:in `parse'
	from (irb):2
	from /usr/bin/irb:12:in `<main>'
irb(main):003:0> JSON.generate(Float::INFINITY)
JSON::GeneratorError: 807: Infinity not allowed in JSON
	from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:224:in `generate'
	from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/json/common.rb:224:in `generate'
	from (irb):3
	from /usr/bin/irb:12:in `<main>'

Also, this StackOverflow answer suggests that un-parsing Infinity on a website can be a security problem: https://stackoverflow.com/a/1424034/5237018

(I guess un-parsing 1e9999 wouldn't have that problem. Anyway this is my 2c --- don't support +inf etc.)

bennn avatar Feb 05 '18 02:02 bennn

Very good points. Thank you for your efforts in researching this.

This hack makes me a little uncomfortable as well, but I pass a lot of numerical data between the web server and browser, so for me inf support is quite useful. For me, the usefulness outweighs the icky factor.

I do see your point about sticking to the json standard, which doesn't support infinite values. I see this as an unfortunate limitation, so this was an easy workaround that I would think would be parsable by most existing json parsers. I prefer 1e999 over 'Infinity' because I think more parsers might support it, but I don't know for sure. Technically 1e9999 is a valid json number (since the float type is not defined), but infinity is not.

What about adding some keyword (#:allow-infinite? #t) and in the docs making it explicit that by allowing infinite numbers we're going outside of the json specification?

wluker avatar Feb 05 '18 16:02 wluker

I like the keyword idea. How about going one step further, and passing an "illegal value converter" through a keyword argument? Something like #:write-expr f where write-json will call f with any value it doesn't know how to convert.

Then, depending on the #:write-expr function, people could extend write-json to handle +inf.0 and +nan.0 and 0+1i and (vector 1) ... and maybe also the issues in #1878 and #1878 ... in a way that makes sense for their programs.

bennn avatar Feb 06 '18 03:02 bennn