Improve documentation

Among other things, document Polymorphic and PolymorphicTypes
annotations in README.
dev
Exlll 2 years ago
parent 482e9464a9
commit 268ce78163

@ -223,9 +223,9 @@ public final class UnsupportedTypes<T> {
``` ```
**NOTE:** Even though this library does not support these types, it is still possible to serialize **NOTE:** Even though this library does not support these types, it is still possible to serialize
them by providing a custom serializer via them by providing a custom serializer via the [`@SerializeWith`](#the-serializewith-annotation)
[the `@SerializeWith` annotation](#the-serializewith-annotation). That serializer then has to annotation. That serializer then has to be applied to top-level type (i.e. `nesting` must be set
be applied to top-level type (i.e. `nesting` must be set to `0`, which is the default). to `0`, which is the default).
</details> </details>
@ -644,6 +644,43 @@ Map<Integer, Map<String, Double>> map;
</details> </details>
#### The `@Polymorphic` annotation
The `@Polymorphic` annotation indicates that the annotated type is polymorphic. Serializers for
polymorphic types are not selected based on the compile-time types of configuration elements, but
instead are chosen at runtime based on the actual types of their values.
This enables adding instances of subclasses / implementations of a polymorphic type to collections.
The subtypes must be valid configurations.
```java
@Polymorphic
@Configuration
static abstract class A { ... }
static final class Impl1 extends A { ... }
static final class Impl2 extends A { ... }
List<A> as = List.of(new Impl1(...), new Impl2(...), ...);
```
For correct deserialization, if an instance of polymorphic type (or one of its implementations /
subclasses) is serialized, an additional property that holds type information is added to its
serialization. By default, that type information is the Java class name of the actual type. It is
possible to provide type aliases by using the `PolymorphicTypes` annotation.
```java
@Polymorphic
@PolymorphicTypes({
@PolymorphicTypes.Type(type = Impl1.class, alias = "IMPL_1"),
@PolymorphicTypes.Type(type = Impl2.class, alias = "IMPL_2")
})
interface B { ... }
record Impl1(...) implements B { ... }
record Impl2(...) implements B { ... }
```
### Custom serializers ### Custom serializers
If you want to add support for a type that is not a Java record or whose class is not annotated If you want to add support for a type that is not a Java record or whose class is not annotated
@ -691,8 +728,8 @@ Instances of the `SerializerContext` interface contain contextual information fo
serializers. A context object gives access to the configuration properties, configuration element, serializers. A context object gives access to the configuration properties, configuration element,
and the annotated type for which the serializer was selected. and the annotated type for which the serializer was selected.
The context object can be accessed when adding a serializer factory through Context objects can be obtained when adding serializer factories through the `addSerializerFactory`
the `addSerializerFactory` method: method:
```java ```java
public final class PointSerializer implements Serializer<Point, String> { public final class PointSerializer implements Serializer<Point, String> {

@ -162,8 +162,9 @@ class ConfigurationProperties {
* If this library already provides a serializer for the given type (e.g. {@code BigInteger}, * If this library already provides a serializer for the given type (e.g. {@code BigInteger},
* {@code LocalDate}, etc.) the serializer created by the factory takes precedence. * {@code LocalDate}, etc.) the serializer created by the factory takes precedence.
* <p> * <p>
* If a serializer is added via {@link #addSerializer(Class, Serializer)} method for the * If a serializer is added via the {@link #addSerializer(Class, Serializer)} method
* same type, the serializer created by the factory added by this method takes precedence. * for the same type, the serializer created by the factory that was added by this
* method takes precedence.
* *
* @param serializedType the class of the type that is serialized * @param serializedType the class of the type that is serialized
* @param serializerFactory the factory that creates a new serializer * @param serializerFactory the factory that creates a new serializer

@ -6,15 +6,18 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* Indicates that the annotated configuration element or type should be serialized using the * Indicates that the annotated configuration element or type should be serialized using
* referenced serializer. * an instance of the referenced serializer. The serializer referenced by this annotation
* is selected regardless of whether the type of configuration element or annotated type
* matches the type the serializer expects.
* <p> * <p>
* If this annotation is applied to a configuration element, and that element is an array, list, * If this annotation is applied to a configuration element, and that element is an array,
* set, or map a nesting level can be set to apply the serializer not to the top-level type but to * list, set, or map a nesting level can be set to apply the serializer not to the
* its elements. For maps, the serializer is applied to the values and not the keys. * top-level type but to its elements. For maps, the serializer is applied to the values
* and not the keys.
* <p> * <p>
* The following example shows how {@code nesting} can be used to apply the serializer at * The following examples show how {@code nesting} can be used to apply the serializer to
* different levels. * configuration elements at different levels.
* *
* <pre> * <pre>
* {@code * {@code
@ -43,6 +46,21 @@ import java.lang.annotation.Target;
* Map<Integer, Map<String, Double>> map; * Map<Integer, Map<String, Double>> map;
* } * }
* </pre> * </pre>
* <p>
* If instead the annotation is applied to a non-generic type, then, whenever that exact
* type (i.e. not a subtype or implementation) is encountered, the referenced serializer
* is selected.
* <pre>
* {@code
* @SerializeWith(serializer = MyClassSerializer.class)
* public final class MyClass { ... }
* }
* </pre>
* <p>
* Similarly, this annotation can be used as a meta-annotation on other {@code ElementType.TYPE}
* annotations. In these cases, the serializer is selected whenever a type annotated with
* the meta-annotated annotation is found. An example for this is the {@link Polymorphic}
* annotation.
*/ */
@Target({ @Target({
ElementType.ANNOTATION_TYPE, // usage as meta-annotation ElementType.ANNOTATION_TYPE, // usage as meta-annotation
@ -62,8 +80,11 @@ public @interface SerializeWith {
/** /**
* Returns the nesting level at which to apply the serializer. * Returns the nesting level at which to apply the serializer.
* <p> * <p>
* If this annotation is applied to a type or another annotation, the value * Setting {@code nesting} to an invalid value, i.e. a negative one or one that is greater than
* returned by this method has no effect. * the number of levels the element actually has, results in the serializer not being selected.
* <p>
* If this annotation is applied to a type or another annotation, the value returned by this
* method has no effect.
* *
* @return the nesting level * @return the nesting level
*/ */

@ -1,17 +1,39 @@
package de.exlll.configlib; package de.exlll.configlib;
import java.lang.reflect.AnnotatedType; import java.lang.reflect.AnnotatedType;
import java.util.function.Function;
/** /**
* Instances of this class provide contextual information for custom serializers. * Instances of this class provide contextual information for custom serializers.
* References to such instances can be obtained when adding serializer factories through
* the {@link ConfigurationProperties.Builder#addSerializerFactory(Class, Function)}
* method.
* <p> * <p>
* Custom serializers classes are allowed to declare a constructor with one parameter of * Custom serializers used with {@code @SerializeWith} are allowed to declare a constructor
* type {@code SerializerContext}. If such a constructor exists, an instance of this class is * with one parameter of type {@code SerializerContext}. If such a constructor exists, a
* passed to it when the serializer is instantiated by this library. * context object is injected into it when the serializer is instantiated.
*
* <pre>
* {@code
* public final class PointSerializer implements Serializer<Point, String> {
* private final SerializerContext context;
*
* public PointSerializer(SerializerContext context) {
* this.context = context;
* }
* // implementation ...
* }
*
* YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder()
* .addSerializerFactory(Point.class, PointSerializer::new)
* .build();
* }
* </pre>
*/ */
public interface SerializerContext { public interface SerializerContext {
/** /**
* Returns the {@code ConfigurationProperties} object in use when the serializer was selected. * Returns the {@code ConfigurationProperties} object in use when the serializer was
* selected.
* *
* @return properties object in use when the serializer was selected * @return properties object in use when the serializer was selected
*/ */
@ -25,11 +47,11 @@ public interface SerializerContext {
ConfigurationElement<?> element(); ConfigurationElement<?> element();
/** /**
* Returns the {@code AnnotatedType} which led to the selection of the serializer. The annotated * Returns the {@code AnnotatedType} which led to the selection of the serializer. The
* type returned by this method might be different from the one returned by * annotated type returned by this method might be different from the one returned by
* {@link ConfigurationElement#annotatedType()}. Specifically, the type is different when the * {@link ConfigurationElement#annotatedType()}. Specifically, the type is different
* serializer is applied to a nested type via {@link SerializeWith} in which case the annotated * when the serializer is applied to a nested type via {@link SerializeWith} in which
* type represents the type at that nesting level. * case the annotated type represents the type at that nesting level.
* *
* @return annotated type which led to the selection of the serializer * @return annotated type which led to the selection of the serializer
*/ */

Loading…
Cancel
Save