Multiple Serialization Contexts

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):

  1. If adapter extends TypeAdapter then give back as is. TypeAdapter will construct the appropiate serializer and – wishfully – return the correct value.
  2. If adapter doesn’t implement JsonSerializer then check for XmlTransient and if not present use default mechanism. Serialize null otherwise.
  3. If adapter doesn’t implement SerializationAdapterCondition serialize with JsonSerializer
  4. If it does, ask for permission to use serializer. If positive serialize with JsonSerializer
  5. If not, check for for XmlTransient and if not present use default mechanism. Serialize null 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;
    }

}

Leave a Reply