使用Jackson将JSON正确反序列化为多态类型的问题
Alright, let's walk through how to properly deserialize that JSON into your ItemResponseDto class—this is a typical case when dealing with abstract types and custom type markers in JSON payloads. I'll focus on using Jackson (the go-to JSON library for Java) since it's the most widely adopted, but core concepts can adapt to other libraries if needed.
1. Fix the Abstract Class Type Handling
The biggest challenge here is deserializing the AbstractItem field, since your JSON uses an @class property to specify the concrete implementation (com.somePackage.model.SomeObject). Jackson doesn't recognize @class by default, so we need to configure it explicitly.
Add these annotations to your abstract class and its concrete implementations:
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; // Tell Jackson to use the "@class" property to determine the concrete type @JsonTypeInfo( use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class" ) public abstract class AbstractItem { // Your abstract class fields/methods here } // Map the concrete class to its fully qualified name from the JSON @JsonTypeName("com.somePackage.model.SomeObject") public class SomeObject extends AbstractItem { private List<String> comments; private List<ObjectSmt> otherObjects; // Required no-arg constructor for Jackson public SomeObject() {} // Getters and setters (or use @JsonProperty if you prefer field access) public List<String> getComments() { return comments; } public void setComments(List<String> comments) { this.comments = comments; } public List<ObjectSmt> getOtherObjects() { return otherObjects; } public void setOtherObjects(List<ObjectSmt> otherObjects) { this.otherObjects = otherObjects; } } // Repeat the pattern for ObjectSmt if it also extends an abstract class @JsonTypeInfo( use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class" ) public abstract class AbstractObjectSmt {} @JsonTypeName("com.somePackage.model.ObjectSmt") public class ObjectSmt extends AbstractObjectSmt { // Your ObjectSmt fields, no-arg constructor, getters/setters }
2. Ensure the Warnings Field Maps Correctly
Your JSON has an empty warnings object ({}), and your DTO uses a Map<String, List<String>>. Jackson will handle this by default, but we can make it more robust:
public class ItemResponseDto { private Map<String, List<String>> warnings; private AbstractItem item; // Required no-arg constructor for Jackson public ItemResponseDto() { // Initialize warnings to avoid nulls this.warnings = new HashMap<>(); } // Getters and setters public Map<String, List<String>> getWarnings() { return warnings; } public void setWarnings(Map<String, List<String>> warnings) { this.warnings = warnings; } public AbstractItem getItem() { return item; } public void setItem(AbstractItem item) { this.item = item; } }
3. Execute the Deserialization
Set up your ObjectMapper and run the deserialization:
import com.fasterxml.jackson.databind.ObjectMapper; public class DeserializationDemo { public static void main(String[] args) throws Exception { String jsonPayload = "{ \"warnings\": {}, \"item\": { \"@class\": \"com.somePackage.model.SomeObject\", \"comments\": [ ], \"otherObjects\": [{ \"@class\": \"com.somePackage.model.ObjectSmt\" }] } }"; ObjectMapper mapper = new ObjectMapper(); // Optional: Allow Jackson to access private fields if you skip getters/setters // mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); ItemResponseDto response = mapper.readValue(jsonPayload, ItemResponseDto.class); // Verify the results System.out.println("Warnings map size: " + response.getWarnings().size()); // Should be 0 System.out.println("Concrete item type: " + response.getItem().getClass().getName()); // Should be com.somePackage.model.SomeObject } }
Common Troubleshooting Tips
- InvalidDefinitionException: Double-check that your abstract class has the
@JsonTypeInfoannotation, and that the fully qualified class names in@JsonTypeNamematch exactly what's in the JSON. - Null warnings map: Ensure your
ItemResponseDtoconstructor initializes the map, or addmapper.setDefaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.AS_EMPTY))to convert nulls to empty collections. - Null lists (comments/otherObjects): Initialize the list fields in your concrete classes (e.g.,
private List<String> comments = new ArrayList<>();) to avoid nulls from empty JSON arrays.
内容的提问来源于stack exchange,提问作者Алексей Чикпик

