Parsing result of numbers depends on `allownan`
When allownan = true all numbers are parsed as Float64
julia> JSON.parse("1")
1
julia> JSON.parse("1", allownan = true)
1.0
julia> JSON.parse("""[1,2,3]""")
3-element Vector{Any}:
1
2
3
julia> JSON.parse("""[1,2,3]""", allownan=true)
3-element Vector{Any}:
1.0
2.0
3.0
@quinnj are you planning to address this issue? Should I have a look myself?
This currently is working as designed. There is no explicit control over forcing numbers to be parsed as int/float, but with allownan, it is currently assumed that since you'll be encountering IEEE values (NaN, Inf) that we'll just parse everything as Float64. If you have a specific use-case that requires otherwise, please provide a detailed walk-through/example and we can consider our options.
I don't have a specific use case, but the old versions of JSON and JSON3 used to parse numbers without decimal separator to Int and with to Float64, independent of any allownan flag. Now the existence of allownan breaks with this behaviour and I think many people will find it puzzling.
My main problem with this is that programs that work without allownan might stop working with allownan set.
Assume, I have a payload = """{"reals": [1, 2, 3.1], "ints": [1, 2, 3]}""" which I decide to enhance to also accept Infinity in reals. If I add allownan I will no longer receive a vector of Ints.
julia> payload = """{"reals": [1, 2, 3.1], "ints": [1, 2, 3]}"""
"{\"reals\": [1, 2, 3.1], \"ints\": [1, 2, 3]}"
julia> JSON.parse(payload)
JSON.Object{String, Any} with 2 entries:
"reals" => Any[1, 2, 3.1]
"ints" => Any[1, 2, 3]
julia> JSON.parse(payload, allownan=true)
JSON.Object{String, Any} with 2 entries:
"reals" => Any[1.0, 2.0, 3.1]
"ints" => Any[1.0, 2.0, 3.0]
The current version of JSON still has the behavior of parsing Ints as ints and floats as floats by default. "infinity" and "nan" are inherently IEEE concepts, hence with allownan=true, we switch all parsing to Float64, since that's most likely what you're dealing with.
I don't think the argument that it drastically changes behavior is very strong: it's fairly straightforward to type your vector as Vector{Real}, or do do a quick map(x -> isinteger(x) ? Int(x) : x, reals) if you need integers. There's also always the ability to still get the user-desired types out by doing typed parsing:
julia> struct Foo
reals::Vector{Real}
ints::Vector{Int}
end
julia> JSON.parse("{\"reals\": [1.0, 2.0, 3.1], \"ints\": [1.0, 2.0, 3.0]}", Foo; allownan=true)
Foo(Real[1.0, 2.0, 3.1], [1, 2, 3])
Well, I'll live with it then. But it takes extra time and extra memory.
Let me try another argument, though (I promise, it's my last one).
In JSON you parse "3" to Int and "3.0" to Float64. I like that choice very much, because it interprets numbers just the way Julia parses numbers. That's perfect! Everything else is handled by automatic type conversion and type promotion. I'm happy to have this back.
But why should adding another possible result type change this approach? Just treat each result as if it was a Julia string to be parsed; no decimal point = Int, decimal point => Float64, nan/inf-pattern => Float64.
BTW, in JSON3 both "3" and "3.0" were parsed as "3". But adding the allowinf parameter in JSON3 did not change any parsing behavior; it just parsed "Infinity" as Inf etc. and all the rest was not affected.
If we'd think about optional parsing of complex numbers, we wouldn't parse 1 as 1 + 0im, just because we added allowcomplex = true, would we?
EDIT: As a co-author of GenieFramework I can tell that we do not know before parsing, what the model value should be updated. Hence, we cannot parse to a given type. So if we want to support transmission of Infinity we need to enable that for all parser calls. But with the current version 1 of JSON we will not be able to receive any Integer arrays. That's very unfortunate.
I got around that now in GenieFramework.