Spring Boot+Angular电商系统Map反序列化错误排查求助
Spring Boot JSON解析错误(Map<String,String>字段反序列化失败)
错误现象
- 触发场景:添加新商品时返回400 Bad Request,控制器方法未执行(参数解析阶段提前失败)
- 核心错误日志:
2024-11-22T15:47:36.290+01:00 WARN 12532 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `java.util.LinkedHashMap` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"par1":"par2"}')]
- 背景:将
ProductEntity、ProductDTO、ProductFormDTO中的parameters字段从String改为Map<String,String>后出现问题,PostgreSQL中该字段为jsonb类型,此前用String时功能正常。尝试RequestLoggingFilter记录请求但无输出。
解决方案
1. 修正前端请求格式(核心问题)
错误根源是前端发送的parameters字段是字符串形式的JSON(例如{"parameters": "{\"par1\":\"par2\"}"}),而后端期望接收JSON对象形式({"parameters": {"par1":"par2"}})。
验证请求内容的方法:
- 在
application.yml或application.properties中添加日志配置,打印完整请求体:logging: level: org.springframework.web.servlet.DispatcherServlet: DEBUG - 用Postman直接模拟请求:发送
{"parameters": {"par1":"par2"}}格式的JSON,验证后端是否能正常接收。
2. 修复RequestLoggingFilter不生效问题
仅用@WebFilter无法正确注册,需通过配置类注册Bean并开启日志:
@Configuration public class RequestLoggingConfig { @Bean public RequestLoggingFilter requestLoggingFilter() { RequestLoggingFilter filter = new RequestLoggingFilter(); filter.setIncludeClientInfo(true); filter.setIncludeQueryString(true); filter.setIncludePayload(true); filter.setMaxPayloadLength(10000); // 调整为足够容纳请求体的长度 filter.setIncludeHeaders(false); filter.setAfterMessagePrefix("REQUEST DETAILS: "); return filter; } }
同时在配置文件中开启Filter日志:
logging: level: org.springframework.web.filter.RequestLoggingFilter: DEBUG
3. 后端兼容字符串与JSON对象解析(可选)
如果前端暂时无法修改请求格式,可通过自定义Jackson反序列化器兼容两种格式:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.type.MapType; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class FlexibleMapDeserializer extends StdDeserializer<Map<String, String>> { private static final ObjectMapper MAPPER = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); public FlexibleMapDeserializer() { super(Map.class); } @Override public Map<String, String> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getText(); try { // 尝试直接解析为Map MapType mapType = MAPPER.getTypeFactory().constructMapType(HashMap.class, String.class, String.class); return MAPPER.readValue(value, mapType); } catch (Exception e) { // 失败则将字符串作为JSON解析 return MAPPER.readValue(value, new com.fasterxml.jackson.core.type.TypeReference<Map<String, String>>() {}); } } }
在DTO的parameters字段上添加注解:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @JsonDeserialize(using = FlexibleMapDeserializer.class) private Map<String, String> parameters;
4. 数据库映射验证
确保JPA能正确将jsonb字段映射到Map<String,String>:
- 引入Hibernate Types依赖(根据Hibernate版本调整,例如5.x用
hibernate-types-52):
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-types-52</artifactId> <version>2.20.0</version> </dependency>
- 在
ProductEntity的parameters字段上添加@Type注解:
import org.hibernate.annotations.Type; import javax.persistence.Column; @Column(columnDefinition = "jsonb") @Type(type = "jsonb") private Map<String, String> parameters;
内容的提问来源于stack exchange,提问作者lordlaurent
相关产品推荐
相关产品推荐

