Coincidentally I am working on exactly the same problem. And you can actually do this with CustomConverters. Maybe not exactly like you want but in a very similar way. The only problem I have been unable to overcome is that you can't dynamically choose Converters at runtime. This is due to implementation of CustomConversions#registerConversion
on line number 182
.
This example shows what I am currently working on. I am using a GenericConverter
but you can also fallback to regular Converters
. This example doesn't check on Field annotations. Rather it uses an interface called SingleValue
. All of my Value Objects that represent a single value implement this interface. So no need for an annotation on the Field.
@Configuration
@Slf4j
public class MongoDbConfiguration {
@Bean
@Primary
public CustomConversions mongoCustomConversions() throws Exception {
final List converterList = new ArrayList<>();
converterList.add(new SingleValueConverter());
return new CustomConversions(converterList);
}
private static class SingleValueConverter implements GenericConverter {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return new HashSet<>(Arrays.asList(new ConvertiblePair[]{
new ConvertiblePair(UUIDEntityReference.class, String.class),
new ConvertiblePair(String.class, FundingProcessId.class)
// put here all of your type conversions (two way!)
}));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
// first check if the instance of this type is an instance of our 'SingleValue' interface
if (source instanceof SingleValue) {
return ((SingleValue) source).getValue();
}
final Class<?> objectType = targetType.getType();
final Constructor<?> constructor = objectType.getConstructor(sourceType.getType());
return constructor.newInstance(source);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Could not convert unexpected type " +
sourceType.getObjectType().getName() + " to " + targetType.getObjectType().getName(), e);
}
}
}
}
This is the most elegant way I could find. Still looking for way to automate the registration of all types. This may be possible with annotations and classpath scanning.
Note that this implementation assumes a couple of things
- You have an interface
SingleValue
with a method getValue
- Each of your 'single-valued' Value classes have a constructor with a single argument of the internal type representation (e.g. String for a UUID)
EDIT: I posted the wrong example code. Updated it