I have a standard polymorphic type Shape
, and I can polymorphically deserialise it using the standard @JsonTypeInfo
mechanism:
import static org.assertj.core.api.Assertions.assertThat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
public class DiscriminatorAliasTest {
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Square.class, name = "square"),
@JsonSubTypes.Type(value = Circle.class, name = "circle")
})
static abstract class Shape {
abstract public String name();
}
static class Square extends Shape {
@JsonProperty("A")
public float width;
@Override
public String name() { return "square"; }
}
static class Circle extends Shape {
@JsonProperty("A")
public float diameter;
@Override
public String name() { return "circle"; }
}
@Test
public void testDiscriminator() throws Exception {
ObjectMapper mapper = new ObjectMapper();
// This passes!
String squareJson = "{\"type\":\"square\", \"A\": 1.0 }";
Shape square = mapper.readerFor(Shape.class).readValue(squareJson);
assertThat(square.name()).isEqualTo("square");
}
}
However, I would like to add ?
as an alias to the discriminator property type
such that the following JSON strings are deserialised by the Shape
abstract class:
{ "type": "square", "A": 1.0 }
AND { "?": "square", "A": 1.0 }
such that this test case would pass
@Test
public void testDiscriminator() throws Exception {
ObjectMapper mapper = new ObjectMapper();
// This passes!
String squareJson = "{\"type\":\"square\", \"A\": 1.0 }";
Shape square = mapper.readerFor(Shape.class).readValue(squareJson);
assertThat(square.name()).isEqualTo("square");
// and this should pass as well, with NO further modification
// to this test case
String squareJsonWithAlternativePropertyName = "{\"?\":\"square\", \"A\": 1.0 }";
Shape alsoSquare = mapper.readerFor(Shape.class).readValue(squareJsonWithAlternativePropertyName);
assertThat(alsoSquare.name()).isEqualTo("square");
}
Why am I trying to do this?: I am trying to deserialise two different existing discriminated union encoding schemes onto the same class hierarchy - unfortunately there is no way in advance to know which scheme the class must deserialise.
Restrictions:
- I cannot use
@JsonTypeInfo(use = Id.DEDUCTION)
. There must be a discriminator property because the property key set cannot be used to disambiguate each class. - I cannot access or manipulate the
ObjectMapper
prior to mapping as this pattern exposes deserialisation implementation details to external modules. I assume this might rule outmodule.setDeserializerModifier
/DelegatingDeserializer
/BeanDeserializerModifier
based solutions, such as the ones suggested in this question and this question. - I cannot manipulate the JSON, or equivalent
JsonNode
structure before it hits theObjectMapper
- I do not want to manually construct the
Shape
instance by hand by destructuringJsonNode
s. I should reasonably expect to utilise the existing deserialisation machinery and annotations ofShape
and its subtypes.
I am assuming these are reasonable restrictions - setting an alias for the discriminator property in Scala's Circe JSON library whilst complying with these restrictions was comparatively a single line change.
Thanks in advance!