fix(serialize-databind): add (optional) support for hierarchical TypeAdapters

This commit is contained in:
Johannes Frohnmeyer 2024-04-12 21:17:17 +02:00
parent 2512ff4834
commit 25259ba3b7
Signed by: Johannes
GPG Key ID: E76429612C2929F4
4 changed files with 21 additions and 8 deletions

View File

@ -15,31 +15,32 @@ public class ObjectMapper {
private final ConcurrentMap<TypeToken<?>, TypeAdapter<?>> typeTokenCache = new ConcurrentHashMap<>();
private final List<TypeAdapterFactory> factories = new ArrayList<>();
private final Map<TypeToken<?>, TypeAdapter<?>> explicitAdapters = new ConcurrentHashMap<>();
private final Map<TypeToken<?>, TypeAdapter<?>> hierarchyAdapters = new ConcurrentHashMap<>();
public ObjectMapper() {
ServiceLoader.load(TypeAdapterFactory.class).forEach(this::registerTypeAdapterFactory);
ServiceLoader.load(TypeAdapter.class).forEach(this::registerTypeAdapter);
}
public ObjectMapper registerTypeAdapter(Type type, TypeAdapter<?> adapter) {
explicitAdapters.put(TypeToken.get(type), Objects.requireNonNull(adapter));
public ObjectMapper registerTypeAdapter(Type type, TypeAdapter<?> adapter, boolean hierarchical) {
(hierarchical ? hierarchyAdapters : explicitAdapters).put(TypeToken.get(type), Objects.requireNonNull(adapter));
return this;
}
public ObjectMapper registerTypeAdapter(TypeAdapter<?> adapter) {
SerializerFor annotation = adapter.getClass().getAnnotation(SerializerFor.class);
if (annotation == null) {
throw new IllegalArgumentException("TypeAdapter must be annotated with @SerializerFor to register it without specifying the target type");
throw new IllegalArgumentException("TypeAdapter must be annotated with @SerializerFor to register it without specifying the target type: " + adapter.getClass().getName());
}
adapter = annotation.nullSafe() ? adapter.nullSafe() : adapter;
for (Class<?> target : annotation.targets()) {
registerTypeAdapter(target, adapter);
registerTypeAdapter(target, adapter, annotation.hierarchical());
}
return this;
}
public ObjectMapper registerTypeAdapterFactory(TypeAdapterFactory factory) {
factories.add(Objects.requireNonNull(factory));
factories.addFirst(Objects.requireNonNull(factory));
return this;
}
@ -60,6 +61,13 @@ public class ObjectMapper {
if (explicitAdapters.containsKey(type)) {
return (TypeAdapter<T>) explicitAdapters.get(type);
}
for (TypeToken<?> token : hierarchyAdapters.keySet()) {
if (token.isAssignableFrom(type)) {
TypeAdapter<T> adapter = (TypeAdapter<T>) hierarchyAdapters.get(token);
explicitAdapters.put(type, adapter);
return adapter;
}
}
TypeAdapter<?> cached = typeTokenCache.get(type);
if (cached != null) {
return (TypeAdapter<T>) cached;

View File

@ -21,4 +21,9 @@ public @interface SerializerFor {
* adapter. If {@code false}, the adapter will have to handle the {@code null} values.
*/
boolean nullSafe() default true;
/**
* Whether this adapter should also be used for all subtypes of the target type.
*/
boolean hierarchical() default false;
}

View File

@ -24,7 +24,7 @@ public class DefaultDateTypeAdapter extends TypeAdapter<Date> {
*/
private final List<DateFormat> dateFormats = new ArrayList<>();
DefaultDateTypeAdapter() {
public DefaultDateTypeAdapter() {
dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));

View File

@ -438,7 +438,7 @@ public class TypeAdapters {
}
}
@SerializerFor(targets = InetAddress.class)
@SerializerFor(targets = InetAddress.class, hierarchical = true)
public static class InetAddressTypeAdapter extends TypeAdapter<InetAddress> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(InetAddress value, Writer writer) throws TEx, MalformedDataException {
@ -589,7 +589,7 @@ public class TypeAdapters {
}
}
@SerializerFor(targets = DataElement.class)
@SerializerFor(targets = DataElement.class, hierarchical = true)
public static class DataElementTypeAdapter extends TypeAdapter<DataElement> {
@Override
public <TEx extends Throwable, Writer extends SerializeWriter<TEx, Writer>> void serialize(DataElement value, Writer writer) throws TEx, MalformedDataException {