Skip to content

Commit

Permalink
Add dynamic deserialization of AbstractTypes
Browse files Browse the repository at this point in the history
  • Loading branch information
gryumov committed Feb 12, 2025
1 parent a8b7019 commit fc1362c
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 26 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ version = "3.5.0"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
EzXML = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
Expand Down
38 changes: 13 additions & 25 deletions src/De/De.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ function Base.show(io::IO, e::ParamError)
)
end

struct TagError <: DeserError
tag::Any
struct MissingKeyError <: DeserError
key::Any
end

function Base.show(io::IO, e::MissingKeyError)
return print(io, "KeyError: required key '$(e.key)' is missing or invalid.")
end

function Base.show(io::IO, e::TagError)
return print(io, "TagError: tag for method '$(e.tag)' is not declared")
struct UndefKeyError <: DeserError
type::Type
end

function Base.show(io::IO, e::UndefKeyError)
print(io, "UndefKeyError: define `subtype_key(::Type{$(e.type)})` to specify the subtype field.")
end

struct WrongType <: DeserError
Expand All @@ -38,26 +46,6 @@ end

include("Deser.jl")

function tag(::Type{T}, ::Val{x}) where {T,x}
return throw(TagError(x))
end

tag(::Type{T}, ::Nothing) where {T} = T

(tag_key(::Type)::Nothing) = nothing
(tag_val(::Type{T}, ::Nothing, v)::Nothing) where {T} = nothing

function tag_val(::Type{T}, k, v) where {T}
try
Val(Symbol(v[k]))
catch
throw(ParamError(k))
end
end

deser_type(::Type{T}, x) where {T} = tag(T, tag_val(T, tag_key(T), x))
deser_value(::Type{T}, x) where {T} = x

"""
Serde.to_deser(::Type{T}, x) -> T
Expand All @@ -84,7 +72,7 @@ julia> Serde.to_deser(Person, person_data)
Person("Michael", 25, Info(12, 2500))
```
"""
to_deser(::Type{T}, x) where {T} = deser(deser_type(T, x), deser_value(T, x))
to_deser(::Type{T}, x) where {T} = deser(T, x)

to_deser(::Type{Nothing}, x) = nothing
to_deser(::Type{Missing}, x) = missing
Expand Down
16 changes: 15 additions & 1 deletion src/De/Deser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For more details:
"""
abstract type ClassType end

struct AbstractType <: ClassType end
struct CustomType <: ClassType end
struct NullType <: ClassType end
struct PrimitiveType <: ClassType end
Expand Down Expand Up @@ -214,7 +215,7 @@ See also [`Serde.nulltype`](@ref), [`Serde.default_value`](@ref), [`Serde.isempt
## Examples:
Let's make a custom type `Computer` with the following fields and constructor.
Let's make a custom type `Computer` with the following fields and constructor.
```julia
struct Computer
cpu::String
Expand Down Expand Up @@ -411,6 +412,19 @@ end
end
end

subtype_key(::Type{T}) where {T<:Any} = nothing

function deser(::AbstractType, ::Type{T}, data::AbstractDict{K,D})::T where {T,K,D}
field = subtype_key(T)
field === nothing && throw(UndefKeyError(T))
for sub in subtypes(T)
if nameof(sub) == Symbol(data[K(field)])
return deser(CustomType(), sub, data)
end
end
throw(MissingKeyError(field))
end

function deser(::CustomType, ::Type{T}, data::AbstractVector{A})::T where {T<:Any,A<:Any}
veclen = fieldcount(T)
target = Vector{Any}(undef, veclen)
Expand Down
2 changes: 2 additions & 0 deletions src/Serde.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Serde

using InteractiveUtils

function deser end
function parse_value end

Expand Down

0 comments on commit fc1362c

Please sign in to comment.