Thanks to visit codestin.com
Credit goes to docs.juliahub.com

StructTypes.AbstractTypeType
StructTypes.StructType(::Type{T}) = StructTypes.AbstractType()

Signal that T is an abstract type, and when deserializing, one of its concrete subtypes will be materialized, based on a "type" key/field in the serialization object.

Thus, StructTypes.AbstractTypes must define StructTypes.subtypes, which should be a NamedTuple with subtype keys mapping to concrete Julia subtype values. You may optionally define StructTypes.subtypekey that indicates which input key/field name should be used for identifying the appropriate concrete subtype. A quick example using the JSON3.jl package should help illustrate proper use of this StructType:

abstract type Vehicle end

struct Car <: Vehicle
    type::String
    make::String
    model::String
    seatingCapacity::Int
    topSpeed::Float64
end

struct Truck <: Vehicle
    type::String
    make::String
    model::String
    payloadCapacity::Float64
end

StructTypes.StructType(::Type{Vehicle}) = StructTypes.AbstractType()
StructTypes.StructType(::Type{Car}) = StructTypes.Struct()
StructTypes.StructType(::Type{Truck}) = StructTypes.Struct()
StructTypes.subtypekey(::Type{Vehicle}) = :type
StructTypes.subtypes(::Type{Vehicle}) = (car=Car, truck=Truck)

# example from StructTypes deserialization
car = JSON3.read("""
{
    "type": "car",
    "make": "Mercedes-Benz",
    "model": "S500",
    "seatingCapacity": 5,
    "topSpeed": 250.1
}""", Vehicle)

Here we have a Vehicle type that is defined as a StructTypes.AbstractType(). We also have two concrete subtypes, Car and Truck. In addition to the StructType definition, we also define StructTypes.subtypekey(::Type{Vehicle}) = :type, which signals that when deserializing, when it encounters the type key, it should use the value, in the above example: car, to discover the appropriate concrete subtype to parse the structure as, in this case Car. The mapping of subtype key value to concrete Julia subtype is defined in our example via StructTypes.subtypes(::Type{Vehicle}) = (car=Car, truck=Truck). Thus, StructTypes.AbstractType is useful when the object to deserialize includes a "subtype" key-value pair that can be used to parse a specific, concrete type; in our example, parsing the structure as a Car instead of a Truck.

StructTypes.ArrayTypeType
StructTypes.StructType(::Type{T}) = StructTypes.ArrayType()

Declare that T should map to an array of ordered elements, homogenous or otherwise.

Types already declared as StructTypes.ArrayType() include:

  • Any subtype of AbstractArray
  • Any subtype of AbstractSet
  • Any Tuple type

So if your type already subtypes these and satifies their interface, things should just work.

Otherwise, the interface to satisfy StructTypes.ArrayType() for deserializing is:

  • T(x::Vector): implement a constructor that takes a Vector argument of values and constructs a T
  • StructTypes.construct(::Type{T}, x::Vector; kw...): alternatively, you may overload the StructTypes.construct method for your type if defining a constructor isn't possible
  • Optional: Base.IteratorEltype(::Type{T}) = Base.HasEltype() and Base.eltype(x::T): this can be used to signal that elements for your type are expected to be a homogenous type

The interface to satisfy for serializing is:

  • iterate(x::T): just iteration over each element is required; note if you subtype AbstractArray and define getindex(x::T, i::Int), then iteration is inherited for your type
StructTypes.BoolTypeType
StructTypes.StructType(::Type{T}) = StructTypes.BoolType()

Declare that T should map to a boolean value.

Types already declared as StructTypes.BoolType() include:

  • Bool

The interface to satisfy for deserializing is:

  • T(x::Bool): define a constructor that takes a single Bool value
  • StructTypes.construct(::Type{T}, x::Bool; kw...): alternatively, you may overload StructTypes.construct

The interface to satisfy for serializing is:

  • Bool(x::T): define a conversion to Bool method
StructTypes.CustomStructType
StructTypes.StructType(::Type{T}) = StructTypes.CustomStruct()

Signal that T has a custom serialization/deserialization pattern that doesn't quite fit StructTypes.DataType or StructTypes.InterfaceType. One common example are wrapper types, where you want to serialize as the wrapped type and can reconstruct T manually from deserialized fields directly. Defining CustomStruct() requires overloading StructTypes.lower(x::T), which should return any serializable object, and optionally overload StructTypes.lowertype(::Type{T}), which returns the type of the lowered object (it returns Any by default). lowertype is used to deserialize an object, which is then passed to StructTypes.construct(T, obj) for construction (which defaults to calling T(obj)).

StructTypes.DataTypeType

A kind of StructType where an object's "data" is made up, at least in part, by its direct fields. When serializing, appropriate fields will be accessed directly.

StructTypes.DictTypeType
StructTypes.StructType(::Type{T}) = StructTypes.DictType()

Declare that T should map to a dict-like object of unordered key-value pairs, where keys are Symbol, String, or Int64, and values are any other type (or Any).

Types already declared as StructTypes.DictType() include:

  • Any subtype of AbstractDict
  • Any NamedTuple type
  • The Pair type

So if your type subtypes AbstractDict and implements its interface, then it will inherit the DictType definition and serializing/deserializing should work automatically.

Otherwise, the interface to satisfy StructTypes.DictType() for deserializing is:

  • T(x::Dict{Symbol, Any}): implement a constructor that takes a Dict{Symbol, Any} of input key-value pairs
  • StructTypes.construct(::Type{T}, x::Dict; kw...): alternatively, you may overload the StructTypes.construct method for your type if defining a constructor is undesirable (or would cause other clashes or ambiguities)

The interface to satisfy for serializing is:

  • pairs(x): implement the pairs iteration function (from Base) to iterate key-value pairs to be serialized
  • StructTypes.keyvaluepairs(x::T): alternatively, you can overload the StructTypes.keyvaluepairs function if overloading pairs isn't possible for whatever reason
StructTypes.InterfaceTypeType
StructTypes.InterfaceType

An abstract type used in the API for "interface types" to map Julia types to a "standard" set of common types. See docs for the following concrete subtypes for more details:

  • StructTypes.DictType
  • StructTypes.ArrayType
  • StructTypes.StringType
  • StructTypes.NumberType
  • StructTypes.BoolType
  • StructTypes.NullType
StructTypes.MutableType
StructTypes.StructType(::Type{T}) = StructTypes.Mutable()

Signal that T is a mutable struct with an empty constructor for serializing/deserializing. Though slightly less performant than StructTypes.Struct, Mutable is a much more robust method for mapping Julia struct fields for serialization. This technique requires your Julia type to be defined, at a minimum, like:

mutable struct T
    field1
    field2
    field3
    # etc.

    T() = new()
end

Note specifically that we're defining a mutable struct to allow field mutation, and providing a T() = new() inner constructor which constructs an "empty" T where isbitstype fields will be randomly initialized, and reference fields will be #undef. (Note that the inner constructor doesn't need to be exactly this, but at least needs to be callable like T(). If certain fields need to be intialized or zeroed out for security, then this should be accounted for in the inner constructor). For these mutable types, the type will first be initialized like T(), then serialization will take each key-value input pair, setting the field as the key is encountered, and converting the value to the appropriate field value. This flow has the nice properties of: allowing object construction success even if fields are missing in the input, and if "extra" fields exist in the input that aren't apart of the Julia struct's fields, they will automatically be ignored. This allows for maximum robustness when mapping Julia types to arbitrary data foramts that may be generated via web services, databases, other language libraries, etc.

There are a few additional helper methods that can be utilized by StructTypes.Mutable() types to hand-tune field reading/writing behavior:

  • StructTypes.names(::Type{T}) = ((:juliafield1, :serializedfield1), (:juliafield2, :serializedfield2)): provides a mapping of Julia field name to expected serialized object key name. This affects both serializing and deserializing. When deserializing the serializedfield1 key, the juliafield1 field of T will be set. When serializing the juliafield2 field of T, the output key will be serializedfield2. Field name mappings are provided as a Tuple of Tuple{Symbol, Symbol}s, i.e. each field mapping is a Julia field name Symbol (first) and serialized field name Symbol (second).

  • StructTypes.excludes(::Type{T}) = (:field1, :field2): specify fields of T to ignore when serializing and deserializing, provided as a Tuple of Symbols. When deserializing, if field1 is encountered as an input key, it's value will be read, but the field will not be set in T. When serializing, field1 will be skipped when serializing out T fields as key-value pairs.

  • StructTypes.omitempties(::Type{T}) = (:field1, :field2): specify fields of T that shouldn't be serialized if they are "empty", provided as a Tuple of Symbols. This only affects serializing. If a field is a collection (AbstractDict, AbstractArray, etc.) and isempty(x) === true, then it will not be serialized. If a field is #undef, it will not be serialized. If a field is nothing, it will not be serialized. To apply this to all fields of T, set StructTypes.omitempties(::Type{T}) = true. You can customize this behavior. For example, by default, missing is not considered to be "empty". If you want missing to be considered "empty" when serializing your type MyType, simply define:

@inline StructTypes.isempty(::Type{T}, ::Missing) where {T <: MyType} = true
  • StructTypes.keywordargs(::Type{T}) = (field1=(dateformat=dateformat"mm/dd/yyyy",), field2=(dateformat=dateformat"HH MM SS",)): provide keyword arguments for fields of type T that should be passed to functions that set values for this field. Define StructTypes.keywordargs as a NamedTuple of NamedTuples.
StructTypes.NoStructTypeType

Default StructTypes.StructType for types that don't have a StructType defined; this ensures objects must have an explicit StructType to avoid unanticipated issues

StructTypes.NullTypeType
StructTypes.StructType(::Type{T}) = StructTypes.NullType()

Declare that T should map to a "null" value.

Types already declared as StructTypes.NullType() include:

  • nothing
  • missing

The interface to satisfy for serializing is:

  • T(): an empty constructor for T
  • StructTypes.construct(::Type{T}, x::Nothing; kw...): alternatively, you may overload StructTypes.construct

There is no interface for serializing; if a custom type is declared as StructTypes.NullType(), then serializing will be handled specially; writing null in JSON, NULL in SQL, etc.

StructTypes.NumberTypeType
StructTypes.StructType(::Type{T}) = StructTypes.NumberType()

Declare that T should map to a number value.

Types already declared as StructTypes.NumberType() include:

  • Any subtype of Signed
  • Any subtype of Unsigned
  • Any subtype of AbstractFloat

In addition to declaring StructTypes.NumberType(), custom types can also specify a specific, existing number type it should map to. It does this like:

StructTypes.numbertype(::Type{T}) = Float64

In this case, T declares it should map to an already-supported number type: Float64. This means that when deserializing, an input will be parsed/read/deserialiezd as a Float64 value, and then call T(x::Float64). Note that custom types may also overload StructTypes.construct(::Type{T}, x::Float64; kw...) if using a constructor isn't possible. Also note that the default for any type declared as StructTypes.NumberType() is Float64.

Similarly for serializing, Float64(x::T) will first be called before serializing the resulting Float64 value.

StructTypes.StringTypeType
StructTypes.StructType(::Type{T}) = StructTypes.StringType()

Declare that T should map to a string value.

Types already declared as StructTypes.StringType() include:

  • Any subtype of AbstractString
  • The Symbol type
  • Any subtype of Enum (values are written with their symbolic name)
  • Any subtype of AbstractChar
  • The UUID type
  • Any Dates.TimeType subtype (Date, DateTime, Time, etc.)

So if your type is an AbstractString or Enum, then things should already work.

Otherwise, the interface to satisfy StructTypes.StringType() for deserializing is:

  • T(x::String): define a constructor for your type that takes a single String argument
  • StructTypes.construct(::Type{T}, x::String; kw...): alternatively, you may overload StructTypes.construct for your type
  • StructTypes.construct(::Type{T}, ptr::Ptr{UInt8}, len::Int; kw...): another option is to overload StructTypes.construct with pointer and length arguments, if it's possible for your custom type to take advantage of avoiding the full string materialization; note that your type should implement both StructTypes.construct methods, since direct pointer/length deserialization may not be possible for some inputs

The interface to satisfy for serializing is:

  • Base.string(x::T): overload Base.string for your type to return a "stringified" value, or more specifically, that returns an AbstractString, and should implement ncodeunits(x) and codeunit(x, i).
StructTypes.StructType
StructTypes.StructType(::Type{T}) = StructTypes.Struct()
StructTypes.StructType(::Type{T}) = StructTypes.UnorderedStruct()
StructTypes.StructType(::Type{T}) = StructTypes.OrderedStruct()

Signal that T is an immutable type who's fields should be used directly when serializing/deserializing. If a type is defined as StructTypes.Struct, it defaults to StructTypes.UnorderedStruct, which means its fields are allowed to be serialized/deserialized in any order, as opposed to StructTypes.OrderedStruct which signals that serialization/deserialization must occur in its defined field order exclusively. This can enable optimizations when an order can be guaranteed, but care must be taken to ensure any serialization formats can properly guarantee the order (for example, the JSON specification doesn't explicitly require ordered fields for "objects", though most implementations have a way to support this).

For StructTypes.UnorderedStruct, if a field is missing from the serialization, nothing should be passed to the StructTypes.construct method.

For example, when deserializing a Struct.OrderedStruct, parsed input fields are passed directly, in input order to the T constructor, like T(field1, field2, field3). This means that field names may be ignored when deserializing; fields are directly passed to T in the order they're encountered.

Another example, for reading a StructTypes.OrderedStruct() from a JSON string input, each key-value pair is read in the order it is encountered in the JSON input, the keys are ignored, and the values are directly passed to the type at the end of the object parsing like T(val1, val2, val3). Yes, the JSON specification says that Objects are specifically un-ordered collections of key-value pairs, but the truth is that many JSON libraries provide ways to maintain JSON Object key-value pair order when reading/writing. Because of the minimal processing done while parsing, and the "trusting" that the Julia type constructor will be able to handle fields being present, missing, or even extra fields that should be ignored, this is the fastest possible method for mapping a JSON input to a Julia structure. If your workflow interacts with non-Julia APIs for sending/receiving JSON, you should take care to test and confirm the use of StructTypes.OrderedStruct() in the cases mentioned above: what if a field is missing when parsing? what if the key-value pairs are out of order? what if there extra fields get included that weren't anticipated? If your workflow is questionable on these points, or it would be too difficult to account for these scenarios in your type constructor, it would be better to consider the StructTypes.UnorderedStruct or StructTypes.Mutable() options.

struct CoolType
    val1::Int
    val2::Int
    val3::String
end

StructTypes.StructType(::Type{CoolType}) = StructTypes.OrderedStruct()

# JSON3 package as example
@assert JSON3.read("{"val1": 1, "val2": 2, "val3": 3}", CoolType) == CoolType(1, 2, "3")
# note how `val2` field is first, then `val1`, but fields are passed *in-order* to `CoolType` constructor; BE CAREFUL!
@assert JSON3.read("{"val2": 2, "val1": 1, "val3": 3}", CoolType) == CoolType(2, 1, "3")
# if we instead define `Struct`, which defaults to `StructTypes.UnorderedStruct`, then the above example works
StructTypes.StructType(::Type{CoolType}) = StructTypes.Struct()
@assert JSON3.read("{"val2": 2, "val1": 1, "val3": 3}", CoolType) == CoolType(1, 2, "3")
StructTypes.StructTypeType

Abstract super type of various StructTypes; see StructTypes.DataType, StructTypes.CustomType, StructTypes.InterfaceType, and StructTypes.AbstractType for more specific kinds of StructTypes

StructTypes.applyfield!Method
StructTypes.applyfield!(f, x::T, nm::Symbol) => Bool

Convenience function for working with a StructTypes.Mutable object. For a given serialization name nm, apply the function f(i, name, FT; kw...) to the field index i, field name name, field type FT, and any keyword arguments kw defined in StructTypes.keywordargs, setting the field value to the return value of f. Various StructType configurations are respected like keyword arguments, names, and exclusions. applyfield! returns whether f was executed or not; if nm isn't a valid field name on x, false will be returned (important for applications where the input still needs to consume the field, like json parsing). Note that the input nm is treated as the serialization name, so any StructTypes.names mappings will be applied, and the function will be passed the Julia field name.

StructTypes.applyfieldMethod
StructTypes.applyfield(f, ::Type{T}, nm::Symbol) => Bool

Convenience function for working with a StructTypes.Mutable object. For a given serialization name nm, apply the function f(i, name, FT; kw...) to the field index i, field name name, field type FT, and any keyword arguments kw defined in StructTypes.keywordargs. Various StructType configurations are respected like keyword arguments, names, and exclusions. applyfield returns whether f was executed or not; if nm isn't a valid field name on x, false will be returned (important for applications where the input still needs to consume the field, like json parsing). Note that the input nm is treated as the serialization name, so any StructTypes.names mappings will be applied, and the function will be passed the Julia field name.

StructTypes.constructMethod
StructTypes.construct(T, args...; kw...)

Function that custom types can overload for their T to construct an instance, given args... and kw.... The default definition is StructTypes.construct(T, args...; kw...) = T(args...; kw...).

StructTypes.constructMethod
StructTypes.construct(f, T) => T

Apply function f(i, name, FT) over each field index i, field name name, and field type FT of type T, passing the function results to T for construction, like T(x_1, x_2, ...). Note that any StructTypes.names mappings are applied, as well as field-specific keyword arguments via StructTypes.keywordargs.

StructTypes.constructfromFunction
StructTypes.constructfrom(T, obj)
StructTypes.constructfrom!(x::T, obj)

Construct an object of type T (StructTypes.constructfrom) or populate an existing object of type T (StructTypes.constructfrom!) from another object obj. Utilizes and respects StructTypes.jl package properties, querying the StructType of T and respecting various serialization/deserialization names, keyword args, etc.

Most typical use-case is construct a custom type T from an obj::AbstractDict, but constructfrom is fully generic, so the inverse is also supported (turning any custom struct into an AbstractDict). For example, an external service may be providing JSON data with an evolving schema; as opposed to trying a strict "typed parsing" like JSON3.read(json, T), it may be preferrable to setup a local custom struct with just the desired properties and call StructTypes.constructfrom(T, JSON3.read(json)). This would first do a generic parse of the JSON data into a JSON3.Object, which is an AbstractDict, which is then used as a "property source" to populate the fields of our custom type T.

StructTypes.defaultsFunction
StructTypes.defaults(::Type{MyType}) = (:field_a=default_value, :field_b=>default_value)

Define default arguments for various fields of MyType, which will be used to initialize the name-value dictionary used in StructTypes.construct.

StructTypes.excludesFunction
StructTypes.excludes(::Type{T}) = (:field1, :field2)

Specify for a StructTypes.Mutable StructType the fields, given as a Tuple of Symbols, that should be ignored when deserializing, and excluded from serializing.

StructTypes.fieldprefixFunction
StructTypes.fieldprefix(::Type{MyType}, field::Symbol) = :field_

When interacting with database tables and other strictly 2D data formats, objects with aggregate fields must be flattened into a single set of column names. When deserializing a set of columns into an object with aggregate fields, a field type's fieldprefix signals that column names beginning with, in the example above, :field_, should be collected together when constructing the field field of MyType. Note the default definition is StructTypes.fieldprefix(T, nm) = Symbol(nm, :_).

Here's a more concrete, albeit contrived, example:

struct Spouse
    id::Int
    name::String
end

StructTypes.StructType(::Type{Spouse}) = StructTypes.Struct()

struct Person
    id::Int
    name::String
    spouse::Spouse
end

StructTypes.StructType(::Type{Person}) = StructTypes.Struct()
StructTypes.fieldprefix(::Type{Person}, field::Symbol) = field == :spouse ? :spouse_ : :_

Here we have two structs, Spouse and Person, and a Person has a spouse::Spouse. The database tables to represent these entities might look like:

CREATE TABLE spouse (id INT, name VARCHAR);
CREATE TABLE person (id INT, name VARCHAR, spouse_id INT);

If we want to leverage a package like Strapping.jl to automatically handle the object construction for us, we could write a get query like the following to ensure a full Person with field spouse::Spouse can be constructed:

getPerson(id::Int) = Strapping.construct(Person, DBInterface.execute(db,
    """
        SELECT person.id as id, person.name as name, spouse.id as spouse_id, spouse.name as spouse_name
        FROM person
        LEFT JOIN spouse ON person.spouse_id = spouse.id
        WHERE person.id = $id
    """))

This works because the column names in the resultset of this query are "id, name, spouse_id, spouse_name"; because we defined StructTypes.fieldprefix for Person, Strapping.jl knows that each column starting with "spouse_" should be used in constructing the Spouse field of Person.

StructTypes.foreachfieldMethod
StructTypes.foreachfield(f, T) => Nothing

Apply function f(i, name, FT; kw...) over each field index i, field name name, field type FT, and any kw keyword arguments defined in StructTypes.keywordargs for name on type T. Nothing is returned and results from f are ignored. Similar to Base.foreach over collections.

Various "configurations" are respected when applying f to each field:

  • If keyword arguments have been defined for a field via StructTypes.keywordargs, they will be passed like f(i, name, FT, v; kw...)
  • If StructTypes.names has been defined, name will be the serialization name instead of the defined julia field name
  • If a field has been excluded via StructTypes.excludes, it will be skipped
StructTypes.foreachfieldMethod
StructTypes.foreachfield(f, x::T) => Nothing

Apply function f(i, name, FT, v; kw...) over each field index i, field name name, field type FT, field value v, and any kw keyword arguments defined in StructTypes.keywordargs for name in x. Nothing is returned and results from f are ignored. Similar to Base.foreach over collections.

Various "configurations" are respected when applying f to each field:

  • If keyword arguments have been defined for a field via StructTypes.keywordargs, they will be passed like f(i, name, FT, v; kw...)
  • If StructTypes.names has been defined, name will be the serialization name instead of the defined julia field name
  • If a field is undefined or empty and StructTypes.omitempties is defined, f won't be applied to that field
  • If a field has been excluded via StructTypes.excludes, it will be skipped
StructTypes.idpropertyFunction
StructTypes.idproperty(::Type{MyType}) = :id

Specify which field of a type uniquely identifies it. The unique identifier field name is given as a Symbol. Useful in database applications where the id field can be used to distinguish separate objects.

StructTypes.keywordargsFunction
StructTypes.keywordargs(::Type{MyType}) = (field1=(dateformat=dateformat"mm/dd/yyyy",), field2=(dateformat=dateformat"HH MM SS",))

Specify for a StructTypes.Mutable the keyword arguments by field, given as a NamedTuple of NamedTuples, that should be passed to the StructTypes.construct method when deserializing MyType. This essentially allows defining specific keyword arguments you'd like to be passed for each field in your struct. Note that keyword arguments can be passed when reading, like JSON3.read(source, MyType; dateformat=...) and they will be passed down to each StructTypes.construct method. StructTypes.keywordargs just allows the defining of specific keyword arguments per field.

StructTypes.lowerFunction
StructTypes.lower(x::T)

"Unwrap" or otherwise transform x to another object that has a well-defined StructType definition. This is a required method for types declaring StructTypes.CustomStruct. Allows objects of type T to conveniently serialize/deserialize as another type, when their own structure/definition isn't significant. Useful for wrapper types. See also StructTypes.CustomStruct and StructType.lowertype.

StructTypes.lowertypeFunction
StructTypes.lowertype(::Type{T})

For StructTypes.CustomStruct types, they may optionally define lowertype to provide a "deserialization" type, which defaults to Any. When deserializing a type T, the deserializer will first call StructTypes.lowertype(T) = S and proceed with deserializing the type S that was returned. Once S has been deserialized, the deserializer will call StructTypes.construct(T, x::S). With the default of Any, deserializers should return an AbstractDict object where key/values can be enumerated/checked/retrieved to make it decently convenient for CustomStructs to construct themselves.

StructTypes.mapfields!Method
StructTypes.mapfields!(f, x::T)

Applys the function f(i, name, FT; kw...) to each field index i, field name name, field type FT, and any kw defined in StructTypes.keywordargs for name of x, and calls setfield!(x, name, y) where y is returned from f.

This is a convenience function for working with StructTypes.Mutable, where a function can be applied over the fields of the mutable struct to set each field value. It respects the various StructTypes configurations in terms of skipping/naming/passing keyword arguments as defined.

StructTypes.namesFunction
StructTypes.names(::Type{T}) = ((:juliafield1, :serializedfield1), (:juliafield2, :serializedfield2))

Provides a mapping of Julia field name to expected serialized object key name. This affects both reading and writing. When reading the serializedfield1 key, the juliafield1 field of T will be set. When writing the juliafield2 field of T, the output key will be serializedfield2.

StructTypes.omitemptiesFunction
StructTypes.omitempties(::Type{T}) = (:field1, :field2)
StructTypes.omitempties(::Type{T}) = true

Specify for a StructTypes.Mutable StructType the fields, given as a Tuple of Symbols, that should not be serialized if they're considered "empty".

If a field is a collection (AbstractDict, AbstractArray, etc.) and isempty(x) === true, then it will not be serialized. If a field is #undef, it will not be serialized. If a field is nothing, it will not be serialized. To apply this to all fields of T, set StructTypes.omitempties(::Type{T}) = true. You can customize this behavior. For example, by default, missing is not considered to be "empty". If you want missing to be considered "empty" when serializing your type MyType, simply define:

@inline StructTypes.isempty(::Type{T}, ::Missing) where {T <: MyType} = true
StructTypes.@AbstractTypeMacro
@AbstractType(expr::Expr)
@AbstractType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to AbstractType(). If expr is the name of a Type, sets the StructType of that type to AbstractType().

Examples

@AbstractType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@AbstractType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@ArrayTypeMacro
@ArrayType(expr::Expr)
@ArrayType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to ArrayType(). If expr is the name of a Type, sets the StructType of that type to ArrayType().

Examples

@ArrayType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@ArrayType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@BoolTypeMacro
@BoolType(expr::Expr)
@BoolType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to BoolType(). If expr is the name of a Type, sets the StructType of that type to BoolType().

Examples

@BoolType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@BoolType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@CustomStructMacro
@CustomStruct(expr::Expr)
@CustomStruct(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to CustomStruct(). If expr is the name of a Type, sets the StructType of that type to CustomStruct().

Examples

@CustomStruct MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@CustomStruct struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@DictTypeMacro
@DictType(expr::Expr)
@DictType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to DictType(). If expr is the name of a Type, sets the StructType of that type to DictType().

Examples

@DictType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@DictType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@MutableMacro
@Mutable(expr::Expr)
@Mutable(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to Mutable(). If expr is the name of a Type, sets the StructType of that type to Mutable().

Examples

@Mutable MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@Mutable struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@NullTypeMacro
@NullType(expr::Expr)
@NullType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to NullType(). If expr is the name of a Type, sets the StructType of that type to NullType().

Examples

@NullType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@NullType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@NumberTypeMacro
@NumberType(expr::Expr)
@NumberType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to NumberType(). If expr is the name of a Type, sets the StructType of that type to NumberType().

Examples

@NumberType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@NumberType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@OrderedStructMacro
@OrderedStruct(expr::Expr)
@OrderedStruct(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to OrderedStruct(). If expr is the name of a Type, sets the StructType of that type to OrderedStruct().

Examples

@OrderedStruct MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@OrderedStruct struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@StringTypeMacro
@StringType(expr::Expr)
@StringType(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to StringType(). If expr is the name of a Type, sets the StructType of that type to StringType().

Examples

@StringType MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@StringType struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@StructMacro
@Struct(expr::Expr)
@Struct(expr::Symbol)

If expr is a struct definition, sets the StructType of the defined struct to Struct(). If expr is the name of a Type, sets the StructType of that type to Struct().

Examples

@Struct MyStruct

is equivalent to

StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()

and

@Struct struct MyStruct
    val::Int
end

is equivalent to

struct MyStruct
    val::Int
end
StructTypes.StructType(::Type{MyStruct}) = StructType.Struct()
StructTypes.@register_struct_subtypeMacro

Macro to add subtypes for an abstract type without the need for type field. For a given abstract_typeandstruct_subtypegenerates custom lowered NameTuple with all subtype fields and additionalStructTypes.subtypekey` field used for identifying the appropriate concrete subtype.

usage:

abstract type Vehicle end
struct Car <: Vehicle
    make::String
end
StructTypes.subtypekey(::Type{Vehicle}) = :type
StructTypes.subtypes(::Type{Vehicle}) = (car=Car, truck=Truck)
StructTypes.@register_struct_subtype Vehicle Car