Skip to content

Commit 765c3bf

Browse files
authored
add numbertype=Float64 option (#240)
1 parent df5049d commit 765c3bf

File tree

2 files changed

+33
-11
lines changed

2 files changed

+33
-11
lines changed

src/read.jl

+24-11
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ Read JSON.
2525
* `allow_inf`: Allow reading of `Inf` and `NaN` values (not part of the JSON standard). [default `false`]
2626
* `dateformat`: A [`DateFormat`](https://docs.julialang.org/en/v1/stdlib/Dates/#Dates.DateFormat) describing the format of dates in the JSON so that they can be read into `Date`s, `Time`s, or `DateTime`s when reading into a type. [default `Dates.default_format(T)`]
2727
* `parsequoted`: Accept quoted values when reading into a NumberType. [default `false`]
28+
* `numbertype`: Type to parse numbers as. [default `nothing`, which parses numbers as Int if possible, Float64 otherwise]
2829
"""
29-
function read(str::AbstractString; jsonlines::Bool=false, kw...)
30+
function read(str::AbstractString; jsonlines::Bool=false,
31+
numbertype::Union{DataType, Nothing}=nothing, kw...)
32+
3033
buf = codeunits(str)
3134
len = length(buf)
3235
if len == 0
@@ -39,10 +42,18 @@ function read(str::AbstractString; jsonlines::Bool=false, kw...)
3942
@wh
4043
tape = len < 1000 ? Vector{UInt64}(undef, len + 4) :
4144
Vector{UInt64}(undef, div(len, 10))
45+
if numbertype === nothing
46+
checkint = true
47+
elseif numbertype == Float64
48+
checkint = false
49+
else
50+
throw(ArgumentError("numbertype $numbertype is not supported. " *
51+
"Only `nothing` (default) and `Float64` are supported so far."))
52+
end
4253
if jsonlines
43-
pos, tapeidx = jsonlines!(buf, pos, len, b, tape, Int64(1); kw...)
54+
pos, tapeidx = jsonlines!(buf, pos, len, b, tape, Int64(1), checkint; kw...)
4455
else
45-
pos, tapeidx = read!(buf, pos, len, b, tape, Int64(1), Any; kw...)
56+
pos, tapeidx = read!(buf, pos, len, b, tape, Int64(1), Any, checkint; kw...)
4657
end
4758
@inbounds t = tape[1]
4859
if isobject(t)
@@ -74,9 +85,9 @@ const FLOAT_INT_BOUND = 2.0^53
7485

7586
function read!(buf, pos, len, b, tape, tapeidx, ::Type{Any}, checkint=true; allow_inf::Bool=false)
7687
if b == UInt8('{')
77-
return read!(buf, pos, len, b, tape, tapeidx, Object; allow_inf=allow_inf)
88+
return read!(buf, pos, len, b, tape, tapeidx, Object, checkint; allow_inf=allow_inf)
7889
elseif b == UInt8('[')
79-
return read!(buf, pos, len, b, tape, tapeidx, Array; allow_inf=allow_inf)
90+
return read!(buf, pos, len, b, tape, tapeidx, Array, checkint; allow_inf=allow_inf)
8091
elseif b == UInt8('"')
8192
return read!(buf, pos, len, b, tape, tapeidx, String)
8293
elseif b == UInt8('n')
@@ -203,7 +214,7 @@ function read!(buf, pos, len, b, tape, tapeidx, ::Type{Nothing})
203214
end
204215
end
205216

206-
function read!(buf, pos, len, b, tape, tapeidx, ::Type{Object}; kw...)
217+
function read!(buf, pos, len, b, tape, tapeidx, ::Type{Object}, checkint=true; kw...)
207218
objidx = tapeidx
208219
eT = EMPTY
209220
pos += 1
@@ -263,7 +274,7 @@ function read!(buf, pos, len, b, tape, tapeidx, ::Type{Object}; kw...)
263274
@wh
264275
# now positioned at start of value
265276
prevtapeidx = tapeidx
266-
pos, tapeidx = read!(buf, pos, len, b, tape, tapeidx, Any; kw...)
277+
pos, tapeidx = read!(buf, pos, len, b, tape, tapeidx, Any, checkint; kw...)
267278
@eof
268279
b = getbyte(buf, pos)
269280
@wh
@@ -297,7 +308,7 @@ function read!(buf, pos, len, b, tape, tapeidx, ::Type{Object}; kw...)
297308
invalid(error, buf, pos, Object)
298309
end
299310

300-
function read!(buf, pos, len, b, tape, tapeidx, ::Type{Array}; kw...)
311+
function read!(buf, pos, len, b, tape, tapeidx, ::Type{Array}, checkint=true; kw...)
301312
arridx = tapeidx
302313
eT = EMPTY
303314
pos += 1
@@ -317,7 +328,8 @@ function read!(buf, pos, len, b, tape, tapeidx, ::Type{Array}; kw...)
317328
while true
318329
# positioned at start of value
319330
prevtapeidx = tapeidx
320-
pos, tapeidx = read!(buf, pos, len, b, tape, tapeidx, Any, eT != FLOAT && eT != (FLOAT | NULL); kw...)
331+
check_int = checkint ? eT != FLOAT && eT != (FLOAT | NULL) : false
332+
pos, tapeidx = read!(buf, pos, len, b, tape, tapeidx, Any, check_int; kw...)
321333
@eof
322334
b = getbyte(buf, pos)
323335
@wh
@@ -345,7 +357,7 @@ function read!(buf, pos, len, b, tape, tapeidx, ::Type{Array}; kw...)
345357
invalid(error, buf, pos, Array)
346358
end
347359

348-
function jsonlines!(buf, pos, len, b, tape, tapeidx; kw...)
360+
function jsonlines!(buf, pos, len, b, tape, tapeidx, checkint=true; kw...)
349361
arridx = tapeidx
350362
eT = EMPTY
351363
if pos > len
@@ -361,7 +373,8 @@ function jsonlines!(buf, pos, len, b, tape, tapeidx; kw...)
361373
@wh
362374
# positioned at start of value
363375
prevtapeidx = tapeidx
364-
pos, tapeidx = read!(buf, pos, len, b, tape, tapeidx, Any, eT != FLOAT && eT != (FLOAT | NULL); kw...)
376+
check_int = checkint ? eT != FLOAT && eT != (FLOAT | NULL) : false
377+
pos, tapeidx = read!(buf, pos, len, b, tape, tapeidx, Any, check_int; kw...)
365378
@inbounds eT = promoteeltype(eT, gettypemask(tape[prevtapeidx]))
366379
nelem += 1
367380
if pos > len

test/runtests.jl

+9
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,15 @@ roundtrip(x::T) where T = JSON3.read(JSON3.write(x), T)
172172
@test JSON3.read("true") === true
173173
@test JSON3.read("false") === false
174174

175+
# numbertype
176+
@test JSON3.read("1.0", numbertype=Float64) === 1.0
177+
@test JSON3.read("1", numbertype=Float64) === 1.0
178+
@test_throws ArgumentError JSON3.read("1", numbertype=Float32)
179+
@test JSON3.read("{\"hey\":1}", numbertype=Float64).hey === 1.0
180+
@test JSON3.read("[\"hey\",1]", numbertype=Float64)[2] === 1.0
181+
@test JSON3.read("[[1]]", numbertype=Float64)[1][1] === 1.0
182+
@test JSON3.read("1.0"; jsonlines=true, numbertype=Float64)[1] === 1.0
183+
175184
# more robust Int vs. Float detection: https://github.com/quinnj/JSON3.jl/issues/124
176185
@test JSON3.read("9223372036854775807") == 9223372036854775807
177186
@test JSON3.read("9223372036854775808") == 9.223372036854776e18 # overflowed Int64 promotes to Float64 (though slightly lossy)

0 commit comments

Comments
 (0)