jackson-modules-java17-sealed-classes
is a Jackson module that adds improved support for polymorphic serialization of Java 17 sealed classes.
- Add improved support for polymorphic serialization of Java 17 sealed classes
- Change any other aspects of Jackson serialization
To activate this feature, users must register the Jdk17SealedClassesModule
module with the ObjectMapper
. Simply include this library, and add the module to the ObjectMapper
:
ObjectMapper mapper=new ObjectMapper().registerModule(new Jdk17SealedClassesModule());
Without this module, sealed classes must use the @JsonSubTypes
annotation for polymorphic serialization, just like any other class:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value=AlphaSealedExample.class, name="alpha"),
@JsonSubTypes.Type(value=BravoSealedExample.class, name="bravo")})
public sealed class SealedExample permits AlphaSealedExample, BravoSealedExample {
}
public final class AlphaSealedExample extends SealedExample {
private String alpha;
public String getAlpha() {
return alpha;
}
public void setAlpha(String alpha) {
this.alpha = alpha;
}
public SealedExample withAlpha(String alpha) {
setAlpha(alpha);
return this;
}
}
public final class BravoSealedExample extends SealedExample {
private String bravo;
public String getBravo() {
return bravo;
}
public void setBravo(String bravo) {
this.bravo = bravo;
}
public SealedExample withBravo(String bravo) {
setBravo(bravo);
return this;
}
}
However, this is repetitive, since sealed classes already enumerate their subtypes explicitly. With this module, users get polymorphic serialization of sealed classes by using only the @JsonTypeInfo
annotation on the parent sealed class. Users can also add @JsonTypeName
to child classes to customize subtype naming, but it is not required.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
public sealed class SealedExample permits AlphaSealedExample, BravoSealedExample {
}
@JsonTypeName("alpha")
public final class AlphaSealedExample extends SealedExample {
private String alpha;
public String getAlpha() {
return alpha;
}
public void setAlpha(String alpha) {
this.alpha = alpha;
}
public SealedExample withAlpha(String alpha) {
setAlpha(alpha);
return this;
}
}
@JsonTypeName("bravo")
public final class BravoSealedExample extends SealedExample {
private String bravo;
public String getBravo() {
return bravo;
}
public void setBravo(String bravo) {
this.bravo = bravo;
}
public SealedExample withBravo(String bravo) {
setBravo(bravo);
return this;
}
}
Either example would result in the following JSON:
serialize(new AlphaSealedExample().withAlpha("value")) → {"type":"alpha","alpha":"value"}
deserialize({"type":"alpha","alpha":"value"}) → new AlphaSealedExample().withAlpha("value")
serialize(new BravoSealedExample().withBravo("value")) → {"type":"bravo","bravo":"value"}
deserialize({"type":"bravo","bravo":"value"}) → new BravoSealedExample().withBravo("value")
Many thanks to @shartte for his work on FasterXML/jackson-module-kotlin#239, where he implemented a very similar feature for Kotlin. And of course, thank you to @cowtowncoder for maintaining the excellent FasterXML/jackson library for all this time.