Extended deserialization
Serde.jl allows users to define how their custom data will be processed during deserialization.
Serde.deser
— MethodSerde.deser(::Type{T}, data) -> T
Main function of this module which can construct an object of type T
from another object data
. Can deserialize complex object with a deep nesting.
Function deser
supports:
- Deserialization from
Dict
andVector
toStruct
- Deserialization from
Dict
andVector
to Vector ofStruct
- Deserialization from
Dict
toDict
- Typecasting during deserialization
- Default value for struct arguments (see
Serde.default_value
) - Custom name for struct arguments (see
Serde.custom_name
) - Empty type definition (see
Serde.isempty
) - Deserializing
missing
andnothing
(seeSerde.nulltype
)
Examples:
julia> struct Info
id::Int64
salary::Int64
end
julia> struct Person
name::String
age::Int64
info::Info
end
julia> info_data = Dict("id" => 12, "salary" => 2500);
julia> person_data = Dict("name" => "Michael", "age" => 25, "info" => info_data);
julia> Serde.deser(Person, person_data)
Person("Michael", 25, Info(12, 2500))
Custom deserialization behavior
If you need to deserialize non-standard custom data types, it will be useful to define a behavior to handle them.
Serde.deser
— MethodSerde.deser(::Type{T}, ::Type{E}, data::D) -> E
Internal function that is used to deserialize data
to fields with type E
of custom type T
. Supports user overriding for custom types.
This function is not used explicitly and can only be overridden for the deserialization process.
Examples:
Let's make a custom type Order
with fields price
and date
.
using Dates
struct Order
price::Int64
date::DateTime
end
Now, we define a new method Serde.deser
for the custom type Order
. This method will be called for each field of Order
that is of type DateTime
and has been passed a String
value.
function Serde.deser(
::Type{T},
::Type{E},
x::String
)::E where {T<:Order,E<:DateTime}
return DateTime(x)
end
After that, if we try to deserialize a dictionary that has a key date
with a String
value, it will correctly convert the String
to a DateTime
value.
julia> Serde.deser(Order, Dict("price" => 1000, "date" => "2024-01-01T10:20:30"))
Order(1000, DateTime("2024-01-01T10:20:30"))
Empty values handling
We can also determine which data types and their values will be treated as nothing
.
Base.isempty
— FunctionSerde.isempty(::Type{T}, x) -> false
This function determines the condition under which the passed value x
for some custom type T
can be treated as nothing
. Supports user overriding for custom types. Initially, all values are set to false
.
This function is not used explicitly and can only be overridden for the deserialization process.
See also Serde.nulltype
, Serde.default_value
.
Examples:
Let's make a custom type Computer
with the following fields. The gpu
field may be either a String
or Nothing
.
struct Computer
cpu::String
ram::Int64
gpu::Union{Nothing,String}
end
Now, we define a new method Serde.isempty
for the custom type Computer
. This method will be called for each field of Computer
that has been passed a String
value.
function Serde.isempty(::Type{Computer}, x::String)
return x == ""
end
So, if we try to deserialize a dictionary with a key gpu
containing an empty string, it will set a nothing
value for such a field in Computer
.
julia> Serde.deser(Computer, Dict("cpu" => "i7-12900", "ram" => 32, "gpu" => "rtx-4090"))
Computer("i7-12900", 32, "rtx-4090")
julia> Serde.deser(Computer, Dict("cpu" => "i3-12100", "ram" => 16, "gpu" => ""))
Computer("i3-12100", 16, nothing)
Names aliases
Sometimes, the field names of the incoming data structure differ from their intended destination. In this case, it is convenient to specify name aliases.
Serde.custom_name
— FunctionSerde.custom_name(::Type{T}, ::Val{x}) -> x
This function is used to define an alias name for field x
of type T
. Supports user overriding for custom types. Initially, all passed names must be equivalent to the target names. Methods of this function must return a Symbol
or a String
value.
This function is not used explicitly and can only be overridden for the deserialization process.
Examples:
Let's make a custom type Phone
with one field price
.
struct Phone
price::Int64
end
Now, we can define a new method Serde.custom_name
for the type Phone
and its field price
.
function Serde.custom_name(::Type{Phone}, ::Val{:price})
return "cost"
end
After that, if we try to deserialize a dictionary with an alias key "cost"
, it will match with the field price
of type Phone
.
julia> Serde.deser(Phone, Dict("cost" => 1000))
Phone(1000)
Custom default values
We can also define default values for certain data types.
Serde.default_value
— FunctionSerde.default_value(::Type{T}, ::Val{x}) -> nothing
This function is used to define default values for field x
of type T
. Supports user overriding for custom types. Initially, all values are set to nothing
.
This function is not used explicitly and can only be overridden for the deserialization process.
See also Serde.isempty
, Serde.nulltype
.
Examples:
Let's make a custom type TimeZone
with the field gmt
.
struct TimeZone
gmt::String
end
Now, we can define a new method Serde.default_value
for the type TimeZone
and its field gmt
.
function Serde.default_value(::Type{TimeZone}, ::Val{:gmt})
return "UTC+3"
end
After that, if we try to deserialize a dictionary without a key gmt
, it will be filled with the default value "UTC+3"
.
julia> Serde.deser(TimeZone, Dict{String,Any}())
TimeZone("UTC+3")
Null types handling
Finally, we can determine the 'nulltype' for custom types when they are empty or not specified at all.
Serde.nulltype
— FunctionSerde.nulltype(::Type{T}) -> nothing
Defines behavior when the value for a field of type T
is empty (according to Serde.isempty
) or not specified. Supports user overriding for custom types. Initially, for all types, it is set to nothing
(in case of type Missing
, it returns the missing
value).
This function is not used explicitly and can only be overridden for the deserialization process.
See also Serde.isempty
, Serde.default_value
.
Examples
Let's make a custom type Computer
with the following fields.
struct Computer
cpu::String
gpu::String
end
For clarity, we also define the Serde.isempty
method.
Serde.isempty(::Type{Computer}, x::String) = x == ""
Next, we define a new method Serde.nulltype
for the custom type Computer
. This method will be called for each type String
that has been passed to a Serde.deser
method.
Serde.nulltype(::Type{String}) = "N/A"
And, if we try to deserialize a dictionary with values of type String
containing an empty string or not specified at all, it will set a "N/A"
value for such fields in Computer
.
julia> Serde.deser(Computer, Dict("cpu" => "i7-12900", "gpu" => ""))
Computer("i7-12900", "N/A")
julia> Serde.deser(Computer, Dict{String,Any}())
Computer("N/A", "N/A")