OptionalSubtypeSerializationTest.java
package tools.jackson.databind.ext.jdk8;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Test for <a href="https://github.com/FasterXML/jackson-databind/issues/5616">
* [databind#5616]: ObjectWriter Serializes Optionals with SubTypes Incompletely
* </a>
* <p>
* When serializing an {@code Optional<Supertype>} with a {@code TypeReference},
* subtype-specific properties should be included in the output.
*/
public class OptionalSubtypeSerializationTest extends DatabindTestUtil
{
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({
@JsonSubTypes.Type(value = Supertype.Subtype.class, name = "subtype"),
})
interface Supertype {
class Subtype implements Supertype {
public String content = "hello";
}
}
private final ObjectMapper MAPPER = newJsonMapper();
// [databind#5616]: List<Supertype> works correctly (baseline test)
@Test
public void testListWithSubtypeProperties() throws Exception
{
Supertype object = new Supertype.Subtype();
String json = MAPPER.writerFor(new TypeReference<List<Supertype>>() {})
.writeValueAsString(List.of(object));
// This works: subtype property "content" is included
assertEquals("[{\"@type\":\"subtype\",\"content\":\"hello\"}]", json);
}
// [databind#5616]: Optional<Supertype> loses subtype properties
@Test
public void testOptionalWithSubtypeProperties() throws Exception
{
Supertype object = new Supertype.Subtype();
String json = MAPPER.writerFor(new TypeReference<Optional<Supertype>>() {})
.writeValueAsString(Optional.of(object));
// This fails: actual output is '{"@type":"subtype"}' - missing "content" property
assertEquals("{\"@type\":\"subtype\",\"content\":\"hello\"}", json);
}
// Additional test: direct subtype serialization works
@Test
public void testDirectSubtypeSerialization() throws Exception
{
Supertype.Subtype object = new Supertype.Subtype();
String json = MAPPER.writeValueAsString(object);
// Direct serialization includes all properties
assertEquals("{\"@type\":\"subtype\",\"content\":\"hello\"}", json);
}
// Additional test: Optional without TypeReference uses runtime type
@Test
public void testOptionalWithoutTypeReference() throws Exception
{
Supertype object = new Supertype.Subtype();
// When not using TypeReference, it serializes based on actual runtime type
// which is Subtype, so @type is not needed (not serializing as Supertype)
String json = MAPPER.writeValueAsString(Optional.of(object));
// Serializes as Subtype directly (no @type discriminator needed)
assertEquals("{\"content\":\"hello\"}", json);
}
}