Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions src/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,28 @@ StructUtils.lift(st::JSONReadStyle, ::Type{T}, x::PtrString) where {T} =
StructUtils.liftkey(::JSONReadStyle, ::Type{T}, x::AbstractString) where {T<:Integer} = Base.parse(T, x)
StructUtils.liftkey(::JSONReadStyle, ::Type{T}, x::AbstractString) where {T<:AbstractFloat} = Base.parse(T, x)

StructUtils.lift(style::JSONReadStyle, ::Type{T}, x, tags) where {T} = StructUtils.lift(style.style, T, x, tags)
StructUtils.lift(style::JSONReadStyle, ::Type{T}, x) where {T} = StructUtils.lift(style.style, T, x)
_isliftpair(x) = x isa Tuple && !(x isa NamedTuple) && length(x) == 2
_liftresult(x, st) = _isliftpair(x) ? x : (x, StructUtils.defaultstate(st))
_liftresult(x, pos::Int) = _isliftpair(x) ? x : (x, pos)

StructUtils.lift(style::JSONReadStyle, ::Type{T}, x, tags) where {T} =
_liftresult(StructUtils.lift(style.style, T, x, tags), style)
StructUtils.lift(style::JSONReadStyle, ::Type{T}, x) where {T} =
_liftresult(StructUtils.lift(style.style, T, x), style)

function customlazylift(style::JSONReadStyle, ::Type{T}, x::LazyValues, tags) where {T}
inner = style.style
inner isa StructUtils.DefaultStyle && return nothing
m4 = which(StructUtils.lift, Tuple{typeof(inner), Type{T}, typeof(x), typeof(tags)})
if m4.module !== StructUtils
return _liftresult(StructUtils.lift(inner, T, x, tags), skip(x))
end
m3 = which(StructUtils.lift, Tuple{typeof(inner), Type{T}, typeof(x)})
if m3.module !== StructUtils
return _liftresult(StructUtils.lift(inner, T, x), skip(x))
end
return nothing
end

function StructUtils.lift(style::JSONReadStyle, ::Type{T}, x::LazyValues) where {T<:AbstractArray{E,0}} where {E}
m = T(undef)
Expand All @@ -393,6 +413,10 @@ end
function StructUtils.lift(style::JSONReadStyle, ::Type{T}, x::LazyValues, tags=(;)) where {T}
type = gettype(x)
buf = getbuf(x)
if type == JSONTypes.OBJECT || type == JSONTypes.ARRAY
custom = customlazylift(style, T, x, tags)
custom === nothing || return custom
end
if type == JSONTypes.STRING
GC.@preserve buf begin
ptrstr, pos = parsestring(x)
Expand Down Expand Up @@ -436,10 +460,10 @@ function StructUtils.lift(style::JSONReadStyle, ::Type{T}, x::LazyValues, tags=(
val1 = out.value
# big switch here for --trim verify-ability
if val1 isa Object{String,Any}
val, _ = StructUtils.lift(style, T, val1)
val, _ = StructUtils.lift(style, T, val1, tags)
return val, pos
elseif val1 isa Vector{Any}
val, _ = StructUtils.lift(style, T, val1)
val, _ = StructUtils.lift(style, T, val1, tags)
return val, pos
elseif val1 isa String
val, _ = StructUtils.lift(style, T, val1)
Expand Down
10 changes: 10 additions & 0 deletions test/parse.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using JSON, StructUtils, UUIDs, Dates, Test

struct CustomJSONStyle <: JSON.JSONStyle end
struct DateStringStyle <: JSON.JSONStyle end
struct DateObjectStyle <: JSON.JSONStyle end

struct A
a::Int
Expand Down Expand Up @@ -236,6 +238,11 @@ Base.valtype(::DictlikeViaCustomStyle) = Int
StructUtils.addkeyval!(a::DictlikeViaCustomStyle, k, v) = StructUtils.addkeyval!(a.vals, k, v)
StructUtils.dictlike(::CustomJSONStyle, ::Type{DictlikeViaCustomStyle}) = true

JSON.lower(::DateStringStyle, d::Date) = string(d)
JSON.lift(::DateStringStyle, ::Type{Date}, x::String) = Date(x)
JSON.lower(::DateObjectStyle, d::Date) = (; time=string(d))
JSON.lift(::DateObjectStyle, ::Type{Date}, x::JSON.LazyValue) = Date(x.time[])

@testset "JSON.parse" begin
@testset "errors" begin
# Unexpected character in array
Expand Down Expand Up @@ -774,6 +781,9 @@ StructUtils.dictlike(::CustomJSONStyle, ::Type{DictlikeViaCustomStyle}) = true
JSON.lift(::CustomJSONStyle, ::Type{Rational}, x) = Rational(x.num[], x.den[])
@test JSON.parse("{\"num\": 1,\"den\":3}", Rational; style=CustomJSONStyle()) == 1//3
@test JSON.parse("{\"num\": 1,\"den\":3}", Rational; style=CustomJSONStyle(), unknown_fields=:error) == 1//3
# https://github.com/JuliaIO/JSON.jl/issues/434
@test JSON.parse(JSON.json(Date(2023, 1, 1); style=DateStringStyle()), Date; style=DateStringStyle()) == Date(2023, 1, 1)
@test JSON.parse(JSON.json(Date(2023, 1, 1); style=DateObjectStyle()), Date; style=DateObjectStyle()) == Date(2023, 1, 1)
# https://github.com/JuliaIO/JSON.jl/issues/453 - dictlike dispatch on custom JSONStyle must reach user method
let res = JSON.parse("""{"a": 1, "b": 2}""", DictlikeViaCustomStyle; style=CustomJSONStyle())
@test res.vals == Dict("a" => 1, "b" => 2)
Expand Down
Loading