You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 ('{&quot;par1&quot;:&quot;par2&quot;}')]
  • 背景:将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 01:47:32