During a recent project we faced the (not so uncommon) problem of different serialization needs depending on the runtime context for our domain model. We have mechanisms inplace for this – multiple ifs in the serializer or even ConditionalSerialization.
The issue is that I spend most of my time reading code, and I like meaningful class names. We need to find something better.
Serialization is done in a last phase so this couldln’t be tackled cleanly without some additional help. At first groups – the validation JSR303 way – seemed as the best approach but finally we took benefit of the polymorphic typeAdapter approach taken in GSON and it doesn’t seem like a terrible decision. Speaking in other words, when the annotation SerializationAdapter
is used, the way serialization is handled is done using the following sequence (same applies to deserialization):
- If adapter extends
TypeAdapter
then give back as is.TypeAdapter
will construct the appropiate serializer and – wishfully – return the correct value. - If adapter doesn’t implement
JsonSerializer
then check forXmlTransient
and if not present use default mechanism. Serialize null otherwise. - If adapter doesn’t implement
SerializationAdapterCondition
serialize withJsonSerializer
- If it does, ask for permission to use serializer. If positive serialize with
JsonSerializer
- If not, check for for
XmlTransient
and if not present use default mechanism. Serializenull
otherwise
Since we now have a way to decide which context to use we can make a decision chain with the grouping annotation SerializationAdapters
. The order in which they are specified is the order in which they are questioned for acceptance. It obviestly doesn’t make sense to have a list of adapters and have the first one not implement SerializationAdapterCondition
or always return true since the other adapters will never be used. There is also a slight difference with how the single adapter works: if XmlTransient is present it will never fallback to the default mechanism.
Happy coding!
Examples:
@XmlTransient @OneToOne(cascade = CascadeType.PERSIST) @SerializationAdapter(SerializeWhenUsingActionEngine.class) private InitialStatement initialStatement;
public class SerializeWhenUsingActionEngine implements SerializationAdapterCondition { @Inject private IThreadLocalRegistry threadLocalRegistry; @Override public boolean accepted() { return threadLocalRegistry.getValue(IActionEntityService.class) != null; } }