|
9 | 9 | OrderedCollections.OrderedDict{String, Any} |
10 | 10 | end |
11 | 11 |
|
| 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 | + |
12 | 22 | @static if isdefined(JSON, :StructUtils) |
13 | 23 | JSON.StructUtils.lowerkey(::JSON.JSONWriteStyle, x::Module) = string(x) |
14 | 24 | end |
15 | 25 |
|
16 | 26 | @inline typify(x) = x |
| 27 | +typify(x::String) = x == UNDEFINED_PLACEHOLDER[] ? UNDEFINED_REPLACEMENT[] : x |
17 | 28 | 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 |
19 | 29 |
|
20 | 30 | # Entry point for JSON objects |
21 | 31 | @inline function typify(d::AbstractDict{<:Any, Any}) |
|
28 | 38 | end |
29 | 39 |
|
30 | 40 | 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 | + |
31 | 45 | if v isa AbstractDict{<:Any, Any} |
32 | 46 | 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} |
34 | 50 | v[k] = typify!(val) |
35 | 51 | end |
36 | 52 | 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} |
38 | 54 | # 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) |
44 | 60 | 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) |
45 | 64 | 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) |
50 | 70 | end |
51 | 71 | # 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)) |
54 | 75 | try |
55 | | - return convert(Vector{T}, v) |
| 76 | + v = convert(Vector{T}, v) |
56 | 77 | catch |
57 | 78 | end |
58 | 79 | end |
|
0 commit comments