jackson.databind.exc.InvalidDefinitionException cannot deserialize from Object value,Spring boot参数报错

2019-10-11 13:49:59

参考地址 Spring Boot在反序列化过程中:jackson.databind.exc.InvalidDefinitionException cannot deserialize from Object value


错误场景

用Spring boot写了一个简单的RESTful API,在测试POST请求的时候,request body是一个符合对应实体类要求的json串,post的时候报错。

先贴一段error log:

复制代码

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.yucfeng.Entity.EData` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (PushbackInputStream); line: 2, column: 2]
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.9.5.jar:2.9.5]
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451) ~[jackson-databind-2.9.5.jar:2.9.5]
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027) ~[jackson-databind-2.9.5.jar:2.9.5]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1290) ~[jackson-databind-2.9.5.jar:2.9.5]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326) ~[jackson-databind-2.9.5.jar:2.9.5]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.5.jar:2.9.5]

复制代码

先说结论

原因:

原因是我在该实体类中添加了一个为了方便实例化该类用的构造函数,导致JVM不会添加默认的无参构造函数,而jackson的反序列化需要无参构造函数,因此报错。

Response实体类同理。

解决:

在实体类中补上一个无参构造器即可。

Jackson反序列化

过debug,让我们来看这一部分的源码:

Jackson中有个BeanDeserializerBase类,该抽象类的子类BeanDeserializer包含了将字节流反序列化的一系列方法:如deserializeFromObject(JsonParser p, DeserializationContext ctxt)、deserialize(JsonParser p, DeserializationContext ctxt)。

在deserializeFromObject方法中,有这么一条判断语句:

if (this._nonStandardCreation)

  该属性_nonStandardCreation在BeanDeserializerBase类中是这样定义的:

this._nonStandardCreation = this._unwrappedPropertyHandler != null || this._valueInstantiator.canCreateUsingDelegate() || 
this._valueInstantiator.canCreateUsingArrayDelegate() || this._valueInstantiator.canCreateFromObjectWith() || !this._valueInstantiator.canCreateUsingDefault();

  我们把目光放到 this._valueInstantiator.canCreateUsingDefault()上:

public boolean canCreateUsingDefault() {
      return this._defaultCreator != null;
  }

  _defaultCreator表示了该实体类是否含有默认无参构造函数(通过反射机制获得)。

上文提到了,我们添加了新的构造函数,JVM并未给该实体类添加默认无参构造函数,因此_defaultCreator为null。回到最初的判断语句,这里的this._nonStandardCreation便是true。

当没有无参构造函数,BeanDeserializer会怎么处理呢?

它会尝试查找该类是否拥有_unwrappedPropertyHandler或者_externalTypeIdHandler,当然并没有。那么调用deserializeFromObjectUsingNonDefault方法进行deserialize。

复制代码

protected Object deserializeFromObjectUsingNonDefault(JsonParser p, DeserializationContext ctxt) throws IOException {
      JsonDeserializer<Object> delegateDeser = this._delegateDeserializer();
      if (delegateDeser != null) {
          return this._valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt));
      } else if (this._propertyBasedCreator != null) {
          return this._deserializeUsingPropertyBased(p, ctxt);
      } else {
          Class<?> raw = this._beanType.getRawClass();
          return ClassUtil.isNonStaticInnerClass(raw) ? ctxt.handleMissingInstantiator(raw, (ValueInstantiator)null, p, "can only instantiate non-static inner class by using default, no-argument constructor", new Object[0]) : ctxt.handleMissingInstantiator(raw, this.getValueInstantiator(), p, "cannot deserialize from Object value (no delegate- or property-based Creator)", new Object[0]);
      }
  }

复制代码

  在该方法中它判断了两个属性:_delegateDeserializer和_propertyBasedCreator,对于这两个属性我也不是很明白。通过查阅API文档,前者表示委托反序列化Deserializer that is used if delegate-based creator is to be used for deserializing from JSON Object.,后者表示If the bean needs to be instantiated using constructor or factory method that takes one or more named properties as argument(s), this creator is used for instantiation(?)。最后判断一下是否是内部静态类,进入异常处理部分。


  • 2018-12-18 15:33:01

    <![CDATA[]]>和转义字符

      此标记用于xml文档中,我们先来看看使用转义符的情况。我们知道,在xml中,”<”、”>”、”&”等字符是不能直接存入的,否则xml语法检查时会报错,如果想在xml中使用这些符号,必须将其转义为实体,如”&lt;”、”&gt;”、”&amp;”,这样才能保存进xml文档。

  • 2018-12-26 15:06:00

    PHP-FPM运行状态的实时查看及监控详解

    php-fpm和nginx一样内建了一个状态页,对于想了解php-fpm的状态以及监控php-fpm非常有帮助。这篇文章就给大家详细介绍了PHP-FPM运行状态的实时查看及监控,有需要的朋友们可以参考学习,感兴趣的朋友们下面来一起看看吧。

  • 2018-12-26 16:12:56

    nginx+php-fpm模式php内存泄漏探究

    这里要重点说一下第三步骤。第三步涉及到php-fpm进程生命周期的东西。一个php-fpm的生命周期大致是这样的:模块初始化(MINIT)-> 模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN) -> 模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN)……. 模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN)-> 模块关闭(MSHUTDOWN)。在一个php-fpm进程的生命周期里,会有多次的模块激活(RINIT)-> 请求处理 -> 模块停用(RSHUTDOWN)的过程。这个“请求处理”的大致过程是这样的:php读取相应的php文件,对其进行词法分析,生成opcode,zend虚拟机执行opcode。

  • 2019-01-01 21:38:51

    php使用curl设置超时的重要性

    网站登录不了,原因是没有可用的 PHP 子进程来响应新的请求了。这可能是是由于PHP-curl 没有设置超时时间引起的。

  • 2019-01-01 21:42:34

    php-fpm 启动参数及重要配置详解

    如果file_get_contents请求的远程资源如果反应过慢,file_get_contents就会一直卡在那里不会超时。我们知道php.ini 里面max_execution_time 可以设置 PHP 脚本的最大执行时间,但是,在 php-cgi(php-fpm) 中,该参数不会起效。真正能够控制 PHP 脚本最大执行时间的是 php-fpm.conf 配置文件中的request_terminate_timeout参数。