FasterXML / FasterXML/jackson-databind

Polymorphic subtype deduction behaves differently based on order of fields

Open
#3,183 11 comments 2 reactions 0 assignees View on GitHub
polymorphic-deduction
Dominant language
Java
Stars
3.7k
Forks
1.5k
Avg merge
3d 6h
Merged PRs (30d)
28

Description

**Describe the bug**
I'm using @JsonTypeInfo(use = Id.DEDUCTION) and have created a scenario where the mapper behaves differently depending on how I order my fields, which I think should be undesirable behavior.

**Version information**
2.12.3

**To Reproduce**
JUnit 5 test:

```
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.json.JsonMapper;

public class DeductionBasedPolymorphismFieldOrderTest {

@Test
public void test1() throws JsonMappingException, JsonProcessingException {
String json = "{\"kingdom\":\"animal\",\"woof\": \"woof.wav\",\"meow\": \"meow.wav\",\"arf\": \"arf.wav\",\"bark\": \"bark.wav\"}";
Animal animal = new JsonMapper().readValue(json, Animal.class); // Succeeds!
assertTrue(animal.getClass().getName().contains("CatDog"));
}

@Test
public void test2() throws JsonMappingException, JsonProcessingException {
String json = "{\"kingdom\":\"animal\",\"meow\": \"meow.wav\",\"woof\": \"woof.wav\",\"arf\": \"arf.wav\",\"bark\": \"bark.wav\"}";
Animal animal = new JsonMapper().readValue(json, Animal.class);
// Throws: UnrecognizedPropertyException: Unrecognized field "woof" (class
// DeductionBasedPolymorphismFieldOrderTest$Cat)
assertTrue(animal.getClass().getName().contains("CatDog"));
}

@Test
public void test3() throws JsonMappingException, JsonProcessingException {
String json = "{\"kingdom\":\"animal\",\"arf\": \"arf.wav\",\"meow\": \"meow.wav\",\"woof\": \"woof.wav\",\"bark\": \"bark.wav\"}";
Animal animal = new JsonMapper().readValue(json, Animal.class);
// Throws: UnrecognizedPropertyException: Unrecognized field "meow" (class
// DeductionBasedPolymorphismFieldOrderTest$Dog2)
assertTrue(animal.getClass().getName().contains("CatDog"));
}

@JsonTypeInfo(use = Id.DEDUCTION, defaultImpl = CatDog.class)
@JsonSubTypes({ @JsonSubTypes.Type(value = Cat.class), @JsonSubTypes.Type(value = Dog1.class),
@JsonSubTypes.Type(value = Dog2.class) })
public static class Animal {
public String kingdom;
}

public static class Cat extends Animal {
public String meow;
}

public static class Dog1 extends Animal {
public String woof;
public String bark;
}

public static class Dog2 extends Animal {
public String woof;
public String arf;
}

public static class CatDog extends Animal {
public String meow;
public String woof;
public String bark;
public String arf;
}
}
```

**Expected behavior**
This situation should always trigger the use of defaultImpl as it does in test 1, or, failing that, throw an InvalidTypeIdException referencing "Cannot deduce unique subtype" (but the behavior should be the same across examples).

**Additional context**
The order of fields should not be what decides subtyping, as it is unintuitive and cannot always be controlled for.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.