Spring Boot JSON 没有反序列化到我的请求模型中

Posted

技术标签:

【中文标题】Spring Boot JSON 没有反序列化到我的请求模型中【英文标题】:SpringBoot JSON not deserializing into my request model 【发布时间】:2021-06-10 23:59:39 【问题描述】:

我正在使用 SpringBoot 并尝试反序列化 JSON,例如:


  "userId": "Dave",
  "queryResults": 
    "id": "ABC",
    "carData": .....,
    "carId": "Honda",
    "status": 0,
    "model": "X"
  

,转入MyRequestModel 类:

public class MyRequestModel 
   private String userId;
   private String: queryResults;

,在我的@PostMapping 方法中作为@RequestBody 参数接收,如下所示:

    @PostMapping
    public String postDate(@RequestBody MyRequestModel data) 
        ...
        
        return "posted";
    

上面的queryResults字段应该作为CLOB存储在数据库中。

我遇到的问题是,如果我将此 JSON 发送到我的端点 (PostMapping) 方法,它无法将其反序列化为 MyRequestModel 并且我收到此错误:

无法从 START_OBJECT 令牌中反序列化 java.lang.String 的实例 在 [来源:(PushbackInputStream); line: 3, column: 18] (通过引用链: MyRequestModel["queryResults"])]

【问题讨论】:

【参考方案1】:

我想您问题的真正答案是:如果您需要将 queryResults 属性设为 String,则实现自定义反序列化器。

如果不是,那么请使用 Jonatan 和 Montaser 在其他答案中提出的替代方案之一。

在 Spring Boot 中实现自定义反序列化器相当简单,因为 Jackson 是它的默认序列化器/反序列化器,它提供了一种编写我们自己的反序列化器的简单方法。

首先,创建一个实现StdDeserializer<T>的类:

MyRequestModelDeserializer.java

public class MyRequestModelDeserializer extends StdDeserializer<MyRequestModel> 

    public MyRequestModelDeserializer() 
        this(null);
    

    public MyRequestModelDeserializer(Class<?> vc) 
        super(vc);
    

    @Override
    public MyRequestModel deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException 
        JsonNode node = p.getCodec().readTree(p);

        String userId = node.get("userId").asText();
        String queryResults = node.get("queryResults").toString();

        MyRequestModel model = new MyRequestModel();
        model.setQueryResults(queryResults);
        model.setUserId(userId);

        return model;
    


其次,使用@JsonDeserialize 注解,使用您的自定义反序列化器将您的类标记为要反序列化:

MyRequestModel.java

@JsonDeserialize(using = MyRequestModelDeserializer.class)
public class MyRequestModel 
    private String userId;
    private String queryResults;

完成了。

【讨论】:

谢谢。这看起来正是我需要的,但是由于某种原因,queryResults 的 json 对象被检索为空字符串。知道为什么上面从 JSON 节点读取 queryResults 的行会返回空字符串吗? 对我来说工作正常。能否请您给我看一下您的代码,以便我看看您的实现是否有问题? 你能详细说明上面的2个构造函数吗?我读了几篇文章,但没有解释它们的作用以及为什么需要它们。尤其是打了this(null) 的电话?另外,我将.asText() 更改为.toString(),并且正要重新测试,看看是否可行。 。 . .我的错误,答案已编辑。在node.get("queryResults") 上使用toString() 来检索JSON。 asText() 不适用于 JSON 的对象类型字段。 . .现在,对于您的问题,Jackson 的ObjectMapper 在创建反序列化器实例时使用带有this(null) 的默认构造函数。 . .另一个构造函数——MyRequestModelDeserializer(Class&lt;?&gt; vc)——你甚至可以省略它而只做public MyRequestModelDeserializer()this(MyRequestModel.class);。此构造函数用于将处理的类设置为StdDeserializer,以便您可以在运行时访问处理的类型。 谢谢,是的,我改用this(MyRequestModel.class) 并使用.asText() 作为userId(因为这不会添加两个额外的字符,它们是userId 周围的引号,所以我的数据库字段长度不会变成简称)和.toString() 以获取queryResults CLOB 字符串值。非常感谢Matheus~【参考方案2】:

queryResults 在 Java 端是 String,但在 JSON 端是 Object。 如果您将其作为String 发送,您将能够对其进行反序列化:


  "userId": "Dave",
  "queryResults": "foo"

或者如果您创建映射到字段的类:

public class MyRequestModel 
   private String userId;
   private QueryResults queryResults;


public class QueryResults 
   private String id;
   private CarData carData;
   private String carId;
   private Integer status;
   private String model;

或者如果你将它序列化为通用的东西(不推荐):

public class MyRequestModel 
   private String userId;
   private Object queryResults;

public class MyRequestModel 
   private String userId;
   private Map<String, Object> queryResults;

public class MyRequestModel 
   private String userId;
   private JsonNode queryResults;

【讨论】:

非常感谢乔纳坦。我想我可能会考虑在下一次迭代中这样做,但现在,要求是使用 CLOB 将该值作为 CLOB 存储到 DB 中。 据我记得,如果你在JsonNode 上调用toString(),你会得到你需要的东西(见我的最后一个例子)。在这种情况下,您不需要自定义逻辑来反序列化请求,它会更具可读性和灵活性。只需在创建推送到数据库的对象时执行String.valueOf(request.getQueryResults())【参考方案3】:

你有两个选项来反序列化这个请求:-

    queryResults的类型更改为Map&lt;String, Object&gt;,它将接受所有作为keyvalue的对象。 (不推荐)
public class MyRequestModel 
   private String userId;
   private Map<String, Object> queryResults;

    您必须创建一个将queryResults 的结果包装为对象的类。
class QueryResult 

    private String id;

    private Map<String, Object> carData;

    private String carId;

    private Integer status;

    private String model;

    public QueryResult() 

    public QueryResult(String id, Map<String, Object> carData, String carId, Integer status, String model) 
        this.id = id;
        this.carData = carData;
        this.carId = carId;
        this.status = status;
        this.model = model;
    

    public String getId() 
        return id;
    

    public void setId(String id) 
        this.id = id;
    

    public Map<String, Object> getCarData() 
        return carData;
    

    public void setCarData(Map<String, Object> carData) 
        this.carData = carData;
    

    public String getCarId() 
        return carId;
    

    public void setCarId(String carId) 
        this.carId = carId;
    

    public Integer getStatus() 
        return status;
    

    public void setStatus(Integer status) 
        this.status = status;
    

    public String getModel() 
        return model;
    

    public void setModel(String model) 
        this.model = model;
    

并使queryResult的类型如图所示:-

public class MyRequestModel 
   private String userId;
   private QueryResult queryResults;

【讨论】:

非常感谢蒙塔泽。现在,我将不得不坚持使用 String,因为这是要求,但这可能会改变。所以,现在我必须将它作为 CLOB 存储到 DB 中,所以它必须是字符串。

以上是关于Spring Boot JSON 没有反序列化到我的请求模型中的主要内容,如果未能解决你的问题,请参考以下文章

使用spring boot将JSON反序列化为带有通用对象列表的POJO

Spring Boot REST 服务:JSON 反序列化不起作用

Spring Boot JSON解析错误:无法反序列化错误

Spring-boot @RequestBody JSON 到带有日期反序列化示例的对象?

Spring Boot中的JSON技术

Spring boot:JSON将带有时区的日期和时间反序列化为LocalDateTime