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(?)。最后判断一下是否是内部静态类,进入异常处理部分。


  • 2017-05-12 16:33:24

    说说JSON和JSONP,也许你会豁然开朗

      JSON(JavaScript Object Notation)和JSONP(JSON with Padding)虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。我们拿最近比较火的谍战片来打个比方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格式,一个是信息传递双方约定的方法。

  • 2017-05-25 23:18:23

    webpack图片的路径与打包

    刚开始用webpack的同学很容易掉进图片打包这个坑里,比如打包出来的图片地址不对或者有的图片并不能打包进我们的目标文件夹里(bundle)。下面我们就来分析下在webpack项目中图片的应用场景。

  • 2017-06-17 18:33:17

    NodeJs使用asyncAwait两法

    async/await使用同步的方式来书写异步代码,将异步调用的难度降低到接近于0,未来必将大放异彩。然而在当下,由于标准化的缓存步伐,async/await尚在ES7的草案中。为了尝先,特试用了下面两种方式:

  • 2017-06-18 02:26:17

    Incorrect string value: '\xF0\x9F\x98\x84\xF0\x9F

    我们可以看到错误提示中的字符0xF0 0x9F 0x98 0x84 ,这对应UTF-8编码格式中的4字节编码(UTF-8编码规范)。正常的汉字一般不会超过3个字节,为什么为出现4个字节呢?实际上是它对应的是智能手机输入法中的表情。那为什么会报错呢?

  • 2017-06-18 02:34:22

    谈mysql中utf8和utf8mb4区别

    MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。

  • 2017-07-05 09:48:51

    CSS 元素垂直居中的 6种方法

    利用CSS进行元素的水平居中,比较简单,行级元素设置其父元素的text-align center,块级元素设置其本身的left 和 right margins为auto即可。本文收集了六种利用css进行元素的垂直居中的方法,每一种适用于不同的情况,在实际的使用过程中选择某一种方法即可。