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:

  1. if we find the target type does not match the value type, we will call Configurations.from_dict
  2. if Configurations.from_dict is not overloaded, it will try to call Base.convert
  3. if Base.convert doesn't work, a standard conversion failure error will be thrown by Base.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_dictMethod
from_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 FieldTypeConversionErrors errors if Base.convert raises exception

ERROR: MethodError: Cannot `convert` an object of type ...
source
Configurations.from_dictMethod
from_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.

source
Configurations.to_dictMethod
to_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)
source

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)