Extended deserialization
Serde.jl allows users to define how their custom data will be processed during deserialization.
Serde.deser — MethodSerde.deser(::Type{T}, data) -> TMain 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
DictandVectortoStruct - Deserialization from
DictandVectorto Vector ofStruct - Deserialization from
DicttoDict - 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
missingandnothing(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) -> EInternal 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
endNow, 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)
endAfter 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) -> falseThis 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}
endNow, 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 == ""
endSo, 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}) -> xThis 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
endNow, 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"
endAfter 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}) -> nothingThis 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
endNow, 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"
endAfter 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
We can also determine the 'nulltype' for custom types when they are empty or not specified at all.
Serde.nulltype — FunctionSerde.nulltype(::Type{T}) -> nothingDefines 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
endFor 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")