Skip to content

Source Generators

kevin-montrose edited this page Jan 25, 2021 · 1 revision

Using Source Generators

Introduction

Cesil supports generating serializers and deserializers at compile time using Source Generators. This requires adding the Cesil.SourceGenerator package, and adding annotations to (de)serialized types. While using Source Generators does allow for runtime code generation to be avoided, reflection is still necessary to find the generated code.

To use a compile time generated (de)serializer, use the AheadOfTimeTypeDescriber on the Options provided to Configuration. A pre-allocated instance of AheadOfTimeTypeDescriber can be found on TypeDescribers.

Serializers

To generate a serializer for a type, you must attach a GenerateSerializerAttribute to it. The default behavior matches DefaultTypeDescriber - that is that by default public properties with default formatters, and getters will be serialized. You can suppress this behavior by placing IgnoreDataMemberAttribute on these properties.

To include fields, methods, or non-public properties you must use place DataMemberAttribute or SerializerMemberAttribute on the target member.

SerializerMemberAttribute

SerializerMemberAttribute allows for setting explicit names, orders, formatters, and should serializers. Each of these options corresponds to an equivalent behavior of SerializableMembers, which control runtime behavior.

Unlike with direct use of SerializableMember, you cannot provide a delegate for any option. You also cannot provide any members that are not accessible from within the current assembly - in practice this means only public members, internal members in the current assembly, and internal members in assembly with an appropriate InternalsVisibleToAttribute can be used.

When specify a related member (such as as a formatter), you must specify both a type and member name. For example, to specify a custom formatter you would write [SerializerMemberAttribute(FormatterType = typeof(MyType), FormatterMethodName = nameof(MyType.MyFormatter))].

Any accesible member of a type that could be used for a Formatter or ShouldSerialize at runtime may be used at compile time, the same parameter lists and return types are permitted.

Deserializers

To generate a deserializer for a type, you must attach a GenerateDeserializerAttribute to it. The default behavior matches DefaultTypeDescriber - that is that by default public properties with default parsers, and setters will be serialized. You can suppress this behavior by placing IgnoreDataMemberAttribute on these properties.

To include fields, methods, or non-public properties you must use place DataMemberAttribute or DeserializerMemberAttribute on the target member.

Obtaining instances of a deserialized type my be customized by specifying a method on GenerateDeserializerAttribute with the InstanceProviderType and InstanceProviderMethodName properties set. The method must conform to same style expected by InstanceProviders in terms of parameter and return types, and must be accessible from the current assembly. To use a constructor (besides the parameterless constructor) to obtain types, you can attach the DeserializerInstanceProviderAttribute to it. If you use a constructor with parameters as an instance provider, every parameter to it must be annotated with a DeserializerMemberAttribute.

DeserializerMemberAttribute

DeserializerMemberAttribute allows for setting explicit names, orders, parsers, and resets. Each of these options corresponds to an equivalent behavior of DeserializableMembers, which control runtime behavior.

Unlike with direct use of DeserializableMember, you cannot provide a delegate for any option. You also cannot provide any members that are not accessible from within the current assembly - in practice this means only public members, internal members in the current assembly, and internal members in assembly with an appropriate InternalsVisibleToAttribute can be used.

When specify a related member (such as as a parser), you must specify both a type and member name. For example, to specify a custom parser you would write [DeserializerMemberAttribute(ParserType = typeof(MyType), ParserMethodName = nameof(MyType.MyFormatter))].

Any accesible member of a type that could be used for a Parser or Reset at runtime may be used at compile time, the same parameter lists and return types are permitted.

Clone this wiki locally