Type Conversion and Custom Parsing
If you find from_dict
or from_toml
doesn't not support a Julia type, such as Symbol
, this is usually because the corresponding parser or format doesn't support this Julia type natively, in this case you will need to define your own type conversion for this option type by overloading Configurations.from_dict
How does it work?
The type conversion for option types work as following:
- if we find the target type does not match the value type, we will call
Configurations.from_dict
- if
Configurations.from_dict
is not overloaded, it will try to callBase.convert
- if
Base.convert
doesn't work, a standard conversion failure error will be thrown byBase.convert
.
Thus, if Base.convert
is already overloaded, this will just work, or if the conversion rule is contextual based on the option type, one can also overload Configurations.from_dict
, this also avoids potential type piracy.
For serialization, one can overload Configurations.to_dict
, this
The Overloading Interface
from_dict
provides two overloading interface
Configurations.from_dict
— Methodfrom_dict(::Type{OptionType}, ::OptionField{f_name}, ::Type{T}, x) where {OptionType, f_name, T}
For option type OptionType
, convert the object x
to the field type T
and assign it to the field f_name
. Raise FieldTypeConversionError
s errors if Base.convert
raises exception
ERROR: MethodError: Cannot `convert` an object of type ...
Configurations.from_dict
— Methodfrom_dict(::Type{OptionType}, ::Type{T}, x) where {OptionType, T}
For option type OptionType
, convert the object x
to type T
. This is similar to Base.convert(::Type{T}, x)
and will fallback to Base.convert
if not defined.
Configurations.to_dict
— Methodto_dict(::Type{T}, x, option::ToDictOption) where T
Convert x
when x
is inside an option type T
. option
is a set of options to determine the conversion behaviour. this can be overloaded to change the behaviour of to_dict(x; kw...)
.
to_dict(::Type{T}, x) where T
One can also use the 2-arg version when x
is not or does not contain an option type for convenience.
Example
The following is a builtin overload to handle list of options.
function Configurations.to_dict(::Type{T}, x::Vector, option::ToDictOption) where T
if is_option(eltype(x))
return map(p->to_dict(T, p, include_defaults), x)
else
return x
end
end
The following overloads the 2-arg to_dict
to convert all VersionNumber
to a String
for all kinds of option types.
Configurations.to_dict(::Type, x::VersionNumber) = string(x)
Example: Contextual Conversion
julia> using Configurations
julia> @option struct MyOption a::Int b::Symbol end
directly calling from_dict
will have the following error
julia> @option struct MyOption a::Int b::Symbol end
julia> d = Dict{String, Any}( "a" => 1, "b" => "ccc" )
Dict{String, Any} with 2 entries: "b" => "ccc" "a" => 1
julia> from_dict(MyOption, d)
ERROR: FieldTypeConversionError: conversion from String to type Symbol for field b in type Main.MyOption failed
now if we define the following type conversion
julia> Configurations.from_dict(::Type{MyOption}, ::Type{Symbol}, s) = Symbol(s)
it will just work
julia> from_dict(MyOption, d)
Main.MyOption(1, :ccc)