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
34 changes: 30 additions & 4 deletions src/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,30 @@ 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)

struct _NoCustomLazyLift end
const _NO_CUSTOM_LAZY_LIFT = _NoCustomLazyLift()

StructUtils.lift(::JSONStyle, ::Type{T}, ::LazyValues, tags) where {T} = _NO_CUSTOM_LAZY_LIFT
StructUtils.lift(::JSONStyle, ::Type{T}, ::LazyValues) where {T} = _NO_CUSTOM_LAZY_LIFT

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 JSONStyle || return nothing
ret = StructUtils.lift(inner, T, x, tags)
ret === _NO_CUSTOM_LAZY_LIFT || return _liftresult(ret, skip(x))
ret = StructUtils.lift(inner, T, x)
ret === _NO_CUSTOM_LAZY_LIFT || return _liftresult(ret, skip(x))
return nothing
end

function StructUtils.lift(style::JSONReadStyle, ::Type{T}, x::LazyValues) where {T<:AbstractArray{E,0}} where {E}
m = T(undef)
Expand All @@ -401,6 +423,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 @@ -444,10 +470,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
14 changes: 14 additions & 0 deletions test/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ using JSON, StructUtils, UUIDs, Dates, Test

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

struct A
a::Int
Expand Down Expand Up @@ -241,6 +244,13 @@ StructUtils.structlike(::RefValueStyle, ::Type{Base.RefValue{Int}}) = false
StructUtils.lower(::RefValueStyle, x::Base.RefValue{Int}) = x[]
StructUtils.lift(::RefValueStyle, ::Type{Base.RefValue{Int}}, x::Integer) = Ref{Int}(x), nothing

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[])
JSON.lower(::DateMaterializedObjectStyle, d::Date) = (; time=string(d))
JSON.lift(::DateMaterializedObjectStyle, ::Type{Date}, x::JSON.Object) = Date(x["time"])

@testset "JSON.parse" begin
@testset "errors" begin
# Unexpected character in array
Expand Down Expand Up @@ -782,6 +792,10 @@ StructUtils.lift(::RefValueStyle, ::Type{Base.RefValue{Int}}, x::Integer) = Ref{
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)
@test JSON.parse(JSON.json(Date(2023, 1, 1); style=DateMaterializedObjectStyle()), Date; style=DateMaterializedObjectStyle()) == 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