【问题标题】:Why Doesn't Setting Jackson to Accept Empty Lists as Null Work?为什么设置杰克逊不接受空列表为空?
【发布时间】:2020-07-21 09:59:51
【问题描述】:

我的公司有一个使用 Spring Web 的 Java Web 服务,该服务通过 REST API 接受 JSON。我们正在使用 Maven,我们的 Jackson 版本是 2.9。我们正在尝试防止在我们的 API 不期望一个空列表时,当集成器传入一个空列表时引发反序列化异常。

例如,这是我的应用程序类:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@EnableWebMvc
@SpringBootApplication
public class ExampleApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
}

Student.java 类:

import lombok.Data;
import java.util.Map;

@Data
public class Student {

    private String firstName;
    private String lastName;
    private Map<String, String> classGrades;
}

学生控制器:

package com.example.example.controllers;

import com.example.example.models.Student;
import org.springframework.http.RequestEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.stream.Stream;

@RestController()
@RequestMapping(value = "/Students/", produces = "application/json")
public class StudentController {

    @PostMapping(path = "")
    public RequestEntity<Student> createNewStudent(@RequestBody Student student) {

       return null;
    }
}

application.properties:

spring.jackson.deserialization.accept-empty-array-as-null-object=true

pom.xml 包含所有默认依赖项,并添加了以下内容:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
</dependency>

所有其他项目文件都默认为 Spring 初始化程序生成的项目结构。预期的请求正文(使用 Postman 形成的 JSON):

{
  "firstName": "Mark",
  "lastName": "Twain",
  "classGrades": {

  }
}

工作得很好。但是,如果任何字段(尽管在我们的特定情况下,classGrades 字段)收到一个空列表,则会引发 Jackson 反序列化异常。失败的 JSON 请求示例:

{
  "firstName": "Mark",
  "lastName": "Twain",
  "classGrades": []
}

以及抛出的异常:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap` out of START_ARRAY token
 at [Source: (PushbackInputStream); line: 65, column: 28] 

根据项目的github 页面,ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT 选项应该可以为我们解决这个问题。我们尝试直接在ObjectMapper 配置对象和application.properties 中使用以下行进行设置:

spring.jackson.deserialization.accept-empty-array-as-null-object=true

这两种方法似乎都没有生效。我们目前正在对容易出现此问题的字段使用@JsonDeserialize(using = MyCustomDeserializer.class) 注释的解决方法。但是,我们希望能够让我们的所有字段默认将空列表视为 null。

我们是否误解了配置选项并错误地使用了它?有没有办法在我们的应用程序中将空列表视为 null,如果是,我们如何实现这一点?

【问题讨论】:

  • 我无法直接使用ObjectMapper 重现此问题,也无法使用简单的 Spring Boot 应用程序。请提供minimal reproducible example。
  • 我会努力提供这个,虽然这需要一些时间。
  • 仅供参考: JsonInclude.Include.NON_EMPTY 只影响序列化(写入 JSON),不影响反序列化(读取 JSON)。
  • @Savior 抱歉让您久等了。我已经用我认为的代表更新了这个问题。我还是新手,所以如果您需要更多问题,请告诉我。
  • 还有什么你没有给我们看的吗?即使添加了龙目岛,我也无法重现这一点。您是否可能声明了一个不同的 ObjectMapper bean?

标签: java json spring web-services jackson


【解决方案1】:

对于这个示例,我的问题是ExampleApplication 类中的@EnableWebMvc 注释。删除该注释后,我可以成功地将一个空数组发送到我的端点,然后将其作为空对象接收。

注意

我原来的问题仍然存在于我公司的应用程序中,即使删除了注释。但是,这似乎是一个可能与 ...accept-empty-arrays-as-null-object 冲突的不同设置的问题。

【讨论】:

  • 我遇到了完全相同的问题,删除@EnableWebMvc 的相同解决方案解决了这个问题。不知道为什么,也不知道注释的值是什么 - 但似乎其他一切仍然按预期工作。
【解决方案2】:

一个 Map 被序列化为

{ "k1": "v1", "k2": "v2"}

一个空的 Map 被序列化为

{ }

在您的示例中,您尝试将表示空列表或数组的 [] 放入 Map 中,这就是杰克逊抱怨的原因。简单地说,不能将空列表映射到 Map。 这也解释了为什么启用 ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT 没有效果

更新

我错了。 Jackson 可以将空数组映射到 Map。 创建了一个使用 spring.jackson.deserialization.accept-empty-array-as-null-object=true 属性的最小 springboot 示例,并且可以将空数组 [] 映射到 Map

如果需要,请查看here。

【讨论】:

  • 这就是DeserializationFeature 的全部目的。从他们链接的文档中:确定空数组值(JSON 中的“[]”)是否被接受为具有数据绑定的常规 POJO(“bean”)的空值:这在处理以像 PHP 这样的语言有松散的类型并且可能将丢失的对象表示为空数组。
  • @savior 我认为你错了。请注意,示例中的 bean 使用 Map,而 JSON 使用数组/列表。他们不映射。杰克逊的错误对此非常清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多