JSON3.jl parse custom type with NaN

The following works:

struct Foo
    a::Vector{Float64}
    b::Float64
end

JSON3.StructType(::Type{Foo}) = JSON3.Struct()

str = JSON3.write(Foo([NaN, 2.3], NaN), allow_inf=true)
JSON3.read(str, Foo, allow_inf=true)

With a custom type it does not:

struct Foo
    a::Vector{Float64}
    b::Float64
end

JSON3.StructType(::Type{Foo}) = JSON3.CustomStruct()
function JSON3.StructTypes.lower(x::Foo)
    return Dict(
        "a" => x.a,
        "b" => x.b,
    )
end
function JSON3.StructTypes.construct(::Type{Foo}, x)
    Foo(x["a"], x["b"])
end

str = JSON3.write(Foo([NaN, 2.3], NaN), allow_inf=true)
JSON3.read(str, Foo, allow_inf=true)
ERROR: LoadError: ArgumentError: invalid JSON at byte position 6 while parsing type Any: InvalidChar
{"b":NaN,"a":[NaN,2.3]}

Stacktrace:
  [1] invalid(error::JSON3.Error, buf::Base.CodeUnits{UInt8, String}, pos::Int64, T::Type)
    @ JSON3 .julia\packages\JSON3\A0XBc\src\JSON3.jl:29
  [2] read(::StructTypes.UnorderedStruct, buf::Base.CodeUnits{UInt8, String}, pos::Int64, len::Int64, b::UInt8, ::Type{Any}; allow_inf::Bool, kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ JSON3 .julia\packages\JSON3\A0XBc\src\structs.jl:100
  [3] read(::StructTypes.UnorderedStruct, buf::Base.CodeUnits{UInt8, String}, pos::Int64, len::Int64, b::UInt8, ::Type{Any})
    @ JSON3 .julia\packages\JSON3\A0XBc\src\structs.jl:76
  [4] read(::StructTypes.DictType, buf::Base.CodeUnits{UInt8, String}, pos::Int64, len::Int64, b::UInt8, ::Type{Dict{String, Any}}, ::Type{String}, ::Type{Any}; kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ JSON3 .julia\packages\JSON3\A0XBc\src\structs.jl:342
  [5] read(::StructTypes.DictType, buf::Base.CodeUnits{UInt8, String}, pos::Int64, len::Int64, b::UInt8, ::Type{Dict{String, Any}}, ::Type{String}, ::Type{Any})
    @ JSON3 .julia\packages\JSON3\A0XBc\src\structs.jl:291
  [6] read(::StructTypes.DictType, buf::Base.CodeUnits{UInt8, String}, pos::Int64, len::Int64, b::UInt8, ::Type{Dict{String, Any}}; kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ JSON3 .julia\packages\JSON3\A0XBc\src\structs.jl:288
  [7] read
    @ .julia\packages\JSON3\A0XBc\src\structs.jl:288 [inlined]
  [8] read(::StructTypes.UnorderedStruct, buf::Base.CodeUnits{UInt8, String}, pos::Int64, len::Int64, b::UInt8, ::Type{Any}; allow_inf::Bool, kw::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ JSON3 .julia\packages\JSON3\A0XBc\src\structs.jl:77
  [9] #read#37
    @ .julia\packages\JSON3\A0XBc\src\structs.jl:371 [inlined]
 [10] #read#16
    @ .julia\packages\JSON3\A0XBc\src\structs.jl:34 [inlined]
 [11] top-level scope
    @ dev.jl:81
in expression starting at dev.jl:63

Is this a bug or do I do something wrong?

Out of curiosity what is written to the JSON file in the example that works? I don’t believe NaN, infinity, or negative infinity are part of the JSON standard.

1 Like

NaN
But it is true, this is not standard JSON.

Yeah, this is a bug; thanks for reporting. The problem here is because you don’t define a specific StructTypes.lowertype for your custom struct (which is fine), we default to parsing a Dict{String, Any}, and the allow_inf keyword wasn’t getting passed through recursively for nested array/objects. Fix is up here: Fix issue reported on discourse where allow_inf not being passed thro… by quinnj · Pull Request #159 · quinnj/JSON3.jl · GitHub

1 Like