为啥spring boot可以在没有默认构造函数的情况下反序列化类?

Posted

技术标签:

【中文标题】为啥spring boot可以在没有默认构造函数的情况下反序列化类?【英文标题】:Why spring boot can deserialize class without default constructor?为什么spring boot可以在没有默认构造函数的情况下反序列化类? 【发布时间】:2021-03-25 05:26:57 【问题描述】:

我想知道为什么spring boot可以通过Jackson的objectMapper反序列化没有默认构造函数的类,但是当我在单元测试中手动使用objectMapper时,它无法反序列化(com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造xxx 的实例(不存在像默认构造函数一样的创建者):无法从对象值反序列化(没有基于委托或属性的创建者) )。

这是我的控制器:

  @PostMapping("/precise")
  public Resp<List<MatchedOutLineResponseDTO>> getPreciseMatchedOutLine(
  @RequestBody List<MatchedOutLineRequestDTO> request) 

这是我的pojo:

@Getter
public class MatchedOutLineRequestDTO 

   private String uuid;
   private JSONObject outLineGeometry;

   public MatchedOutLineRequestDTO(String uuid, JSONObject outLineGeometry) 
     this.uuid = uuid;
     this.outLineGeometry = outLineGeometry;
   
 

谁能告诉我原因?

【问题讨论】:

您是要序列化(从对象转换为 JSON)还是要反序列化(从 JSON 转换为对象) 这不是很清楚。请提供minimal reproducible example。对于@SunitChatterjee 的观点,你为什么要使用两个 JSON 库? JSON 请求和有问题的单元测试会很有用。 @SotiriosDelimanolis 我认为他的应用程序正在使用 Spring Boot 自动提供的 Object Mapper 库。但是,当他编写单元测试时,Spring Boot 库不可用。所以他可能会在单元测试中直接创建和使用 ObjectMapper @SunitChatterjee 是的,你是对的。我在单元测试中直接使用 objectMapper。 【参考方案1】:
    Json 序列化(对象转 JSON) 不需要无参数构造函数 只需要访问器方法 (get...) 来获取要公开的属性 Json 反序列化(JSON 转对象) 需要无参数构造函数和设置器,因为对象映射器首先使用无参数构造器创建类,然后使用设置器设置字段值。

考虑下面的代码示例

public class Test 
    @Getter
    static class Dummy 
        private String uuid;
        private Date date;
        private JSONObject jsonObject;

        public Dummy(String uuid, Date date, JSONObject jsonObject) 
            this.uuid = uuid;
            this.date = date;
            this.jsonObject = jsonObject;
        
    

    public static void main(String[] args) throws JsonProcessingException 
        ObjectMapper mapper = new ObjectMapper();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("Name", "John Doe");
        jsonObject.put("Age", "Unknown");
        Dummy dummyObj = new Dummy(UUID.randomUUID().toString(), new Date(), jsonObject);

        String jsonStr = mapper.writeValueAsString(dummyObj);
        System.out.println("Serialized JSON = " + jsonStr);
    

如果您运行此代码,您将得到的唯一序列化错误是No serializer found for class org.json.JSONObject

问题在于JSONObject类的序列化。

ObjectMapper 实例的默认配置是只访问 属于公共字段或具有公共 getter 的属性。

因此这是org.json.JSONObject的序列化问题。

可能有几种解决方法

    删除 JsonObject 并改用 Map

    通过添加以下内容配置您的对象映射器以访问私有字段:

    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    

你会看到这样的输出


    "uuid": "15f37c75-9a82-476b-a725-90f3742d3de1",
    "date": 1607961986243,
    "jsonObject": 
        "map": 
            "Age": "Unknown",
            "Name": "John Doe"
        
    

    最后一个选项是忽略失败的字段(如JSONObject)并序列化剩余的字段。您可以通过像这样配置对象映射器来做到这一点:

    mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    

如果你这样做,你会看到如下输出:


    "uuid": "82808d07-47eb-4f56-85ff-7a788158bbb5",
    "date": 1607962486862,
    "jsonObject": 

【讨论】:

我想知道的是,当我使用spring boot时,一旦有json请求进来,spring boot可以自动反序列化json请求。但是如上所示请求类没有默认构造函数,spring boot也可以反序列化json请求。 我还没有尝试在 Spring Boot 中反序列化没有构造函数的类。但是我知道默认情况下,Spring Boot 中的对象映射器带有许多默认配置和注册模块。我们在单元测试中使用的是没有任何此类配置的普通对象映射器。 我们面临的一个类似问题是java.time.LocalDate 的序列化和反序列化。如果我们在 json 字符串中提供 LocalDate 为“2020-01-01”,Spring Boot 能够将其反序列化为 LocalDate。但是,单元测试中的对象映射器无法这样做。我们必须在 Object Mapper 中添加这个模块以使其在单元测试中工作 - mapper.registerModule(new JavaTimeModule()); 所以也许你可以调试你的 Spring Boot 应用程序,获取对象映射器并检查默认情况下所有模块和配置在其中注册的内容

以上是关于为啥spring boot可以在没有默认构造函数的情况下反序列化类?的主要内容,如果未能解决你的问题,请参考以下文章

Spring-boot,无法自动装配类。未找到默认构造函数引发异常

未找到接口 java.util.List Rest API Spring boot 的主构造函数或默认构造函数

为啥没有默认构造函数就不能编译?

错误 C2512:没有适当的默认构造函数可用 - 为啥在构造函数中初始化属性?

Spring Boot:没有从字符串值反序列化的字符串参数构造函数/工厂方法

为啥 C++ 构造函数在继承中需要默认参数?