Spring Boot 2中Jackson反序列化空Map失败报错咨询
Map<String, String> in Spring Boot 2 Hey there, let's break down this deserialization issue you're facing. The core problem here is a mismatch between the JSON input your application is receiving and what Jackson expects for your settings field.
What's Causing the Error?
Your DTO's Map<String, String> settings uses @JsonInclude(value=Include.NON_EMPTY, content=Include.NON_NULL) — but important to note: this annotation only affects serialization (converting Java objects to JSON), not deserialization (turning incoming JSON into Java objects).
The error message tells us Jackson is trying to parse an empty string ('') into a LinkedHashMap, which fails because LinkedHashMap has no constructor that accepts a string as input. This means whatever is sending the JSON (like a frontend or external service) is passing {"settings": ""} instead of a valid Map structure, such as {"settings": {"theme": "dark", "notifications": "enabled"}} or an empty object {"settings": {}}.
Solutions to Resolve This
1. Fix the Input Source (Recommended)
The cleanest fix is to ensure the sender provides a valid Map structure for the settings field. If there are no settings to send, they should either:
- Omit the
settingsfield entirely - Send
null - Send an empty object
{}
All of these options will work seamlessly with your existing @JsonInclude configuration.
2. Custom Deserializer to Handle Empty Strings
If you can't control the input source (e.g., dealing with a legacy system that sends empty strings), you can create a custom Jackson deserializer to convert empty strings into an empty Map.
Here's how to implement it:
- First, create the custom deserializer class:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; public class EmptyStringToEmptyMapDeserializer extends JsonDeserializer<Map<String, String>> { @Override public Map<String, String> deserialize(JsonParser parser, DeserializationContext context) throws IOException { JsonNode node = parser.getCodec().readTree(parser); // Convert empty string input to an empty Map if (node.isTextual() && node.asText().isEmpty()) { return new LinkedHashMap<>(); } // Parse valid Map structures normally return parser.getCodec().treeToValue(node, Map.class); } }
- Add the deserializer annotation to your
settingsfield in the DTO:
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.Map; public class YourDTO { // ... other fields ... @JsonInclude(value = JsonInclude.Include.NON_EMPTY, content = JsonInclude.Include.NON_NULL) @JsonDeserialize(using = EmptyStringToEmptyMapDeserializer.class) private Map<String, String> settings; // ... getters and setters ... }
3. Global Jackson Configuration (Optional)
If you want this behavior to apply to all Map fields across your application, you can configure a global Jackson module. Keep in mind this is less targeted and might have unintended side effects, so use it cautiously.
Key Reminder
Don't mix up the purpose of @JsonInclude: it only controls how Jackson serializes your Java objects to JSON (skipping empty collections or null values). It does not change how Jackson parses incoming JSON into Java objects — that's where deserializers or input validation come into play.
内容的提问来源于stack exchange,提问作者robert trudel

