Skip to content

Commit 7add658

Browse files
author
hhaensel
committed
typify!(): parse __undefined__ as nothing, avoid dynamic dispatch, fix errors, add tests
1 parent 7d62cb3 commit 7add658

2 files changed

Lines changed: 55 additions & 15 deletions

File tree

src/JSONParser.jl

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@ else
99
OrderedCollections.OrderedDict{String, Any}
1010
end
1111

12+
const UNDEFINED_PLACEHOLDER = Ref{String}("__undefined__")
13+
const UNDEFINED_REPLACEMENT = Ref{Any}(nothing)
14+
const UNDEFINED_TYPE = Ref{DataType}(typeof(UNDEFINED_REPLACEMENT[]))
15+
16+
function set_undefined_replacement(x)
17+
UNDEFINED_REPLACEMENT[] = x
18+
UNDEFINED_TYPE[] = typeof(x)
19+
nothing
20+
end
21+
1222
@static if isdefined(JSON, :StructUtils)
1323
JSON.StructUtils.lowerkey(::JSON.JSONWriteStyle, x::Module) = string(x)
1424
end
1525

1626
@inline typify(x) = x
27+
typify(x::String) = x == UNDEFINED_PLACEHOLDER[] ? UNDEFINED_REPLACEMENT[] : x
1728
typify(x::Number) = isinteger(x) && !isa(x, Bool) && typemin(Int) x typemax(Int) ? Int(x) : x
18-
typify!(x::Number) = isinteger(x) && !isa(x, Bool) && typemin(Int) x typemax(Int) ? Int(x) : x
1929

2030
# Entry point for JSON objects
2131
@inline function typify(d::AbstractDict{<:Any, Any})
@@ -28,31 +38,42 @@ end
2838
end
2939

3040
function typify!(@nospecialize(v))
41+
v isa Number && return isinteger(v) && !isa(v, Bool) && typemin(Int) v typemax(Int) ? Int(v) : v
42+
43+
v == UNDEFINED_PLACEHOLDER[] && return UNDEFINED_REPLACEMENT[]
44+
3145
if v isa AbstractDict{<:Any, Any}
3246
for (k, val) in v
33-
if val isa Vector{Any} || val isa AbstractDict || val isa Vector{<:Number} || val isa Number
47+
if val == UNDEFINED_PLACEHOLDER[]
48+
v[k] = UNDEFINED_REPLACEMENT[]
49+
elseif val isa Vector{Any} || val isa AbstractDict || val isa Vector{<:Number} || val isa Number || val isa Vector{<:AbstractString}
3450
v[k] = typify!(val)
3551
end
3652
end
37-
elseif v isa Vector{Any} || v isa Vector{<:Number}
53+
elseif v isa Vector{Any} || v isa Vector{<:Number} || v isa Vector{<:AbstractString}
3854
# Mutate recursively
39-
if Int <: eltype(v)
40-
for i in eachindex(v)
41-
x = v[i]
42-
if x isa Vector{Any} || x isa AbstractDict || x isa Vector{<:Number} || x isa Number
43-
v[i] = typify!(x)
55+
for i in eachindex(v)
56+
x = v[i]
57+
if x == UNDEFINED_PLACEHOLDER[]
58+
if ! (UNDEFINED_TYPE[] <: eltype(v))
59+
v = Vector{Any}(v)
4460
end
61+
v[i] = UNDEFINED_REPLACEMENT[]
62+
elseif x isa Vector{Any} || x isa AbstractDict || x isa Vector{<:Number} || x isa Number || x isa Vector{<:AbstractString}
63+
v[i] = typify!(x)
4564
end
46-
else
47-
if all(isinteger, v) && !any(isa.(v, Bool))
48-
return convert(Vector{Int}, v)
49-
end
65+
end
66+
67+
# if all are Integer and not a single Boolean, return a Vector{Int}
68+
if all(Base.Fix2(isa, Number), v) && all(isinteger, v) && !any(isa.(v, Bool))
69+
return convert(Vector{Int}, v)
5070
end
5171
# Try to promote element types
52-
T = !any(isa.(v, Bool)) ? promote_type(union(map(typeof, v))...) : Any
53-
if T != Any
72+
T = any(isa.(v, Bool)) ? Any : promote_type(union(map(typeof, v))...)
73+
# don't convert to promotetype if T is Any or UNDEFINED_TYPE
74+
if T !== Any && (T !== UNDEFINED_TYPE[] || any(UNDEFINED_REPLACEMENT[] .!= v))
5475
try
55-
return convert(Vector{T}, v)
76+
v = convert(Vector{T}, v)
5677
catch
5778
end
5879
end

test/tests_json_payload.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@
1111
Genie.Requests.jsonpayload("test") |> string
1212
end
1313

14+
route("/jsonroundtrip", method = POST) do
15+
global json_result = Genie.Requests.jsonpayload()
16+
"ok"
17+
end
18+
19+
function roundtrip(x)
20+
global json_result
21+
HTTP.request("POST",
22+
"http://localhost:$port/jsonroundtrip",
23+
[("Content-Type", "application/json")],
24+
Genie.JSONParser.json(Dict(:payload => x))
25+
)
26+
json_result["payload"]
27+
end
28+
1429
port = nothing
1530
port = rand(8500:8900)
1631

@@ -40,6 +55,10 @@
4055
@test response.status == 200
4156
@test String(response.body) == "[1, 2, 3]"
4257

58+
@test roundtrip(Dict(:a => "b")).a == "b"
59+
@test eltype(roundtrip([nothing, 1])) === Union{Int, Nothing}
60+
@test eltype(roundtrip([nothing])) === Any
61+
4362
route("/json-error", method = POST) do
4463
error("500, sorry")
4564
end

0 commit comments

Comments
 (0)