Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.springframework.data.domain.Page: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: java.io.PushbackInputStream@5ebf9e24; line: 2, column: 12] (through reference chain: com.sysware.cloud.plugin.bean.Response["data"]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:216) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2938) ~[jackson-databind-2.8.10.jar:2.8.10] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:96) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.cloud.netflix.feign.support.SpringDecoder.decode(SpringDecoder.java:59) ~[spring-cloud-netflix-core-1.4.2.RELEASE.jar:1.4.2.RELEASE] at org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:45) ~[spring-cloud-netflix-core-1.4.2.RELEASE.jar:1.4.2.RELEASE] at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:165) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:133) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.5.0.jar:?] at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.5.0.jar:?] at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) ~[hystrix-core-1.5.12.jar:1.5.12] at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) ~[hystrix-core-1.5.12.jar:1.5.12] at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.2.0.jar:1.2.0] ... 26 more
大概意思是:不能構造實例org.springframework.data.domain.Page :抽象類型要么需要映射到具體類型,要么有自定義反序列化器,要么用額外的類型信息進行實例化 。
解釋下啊,第一種辦法就是page是一個接口,反序列化需要配置映射到具體的類型在Page類上面加@JsonDeserialize注解,指定反序列化的具體實現,但是Page類並不是我們自定義的類,所以在Page接口上加注解應該是不現實的
第二種辦法就是,需要自定義反序列化器,大概就是說序列化和反序列化是使用默認的readOjbect ,和 writeObject ,但是通過默認的readOject解析不了,需要用戶自定義解析Page的解析。
第三方式就是,自定義抽象類型的實現類,用實現類接受。
我們查找到Page的實現類,框架本身已經提供PageImpl,所以我們換成PageImpl接受返回值。
然而,換成PageImpl接受報如下錯誤:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.springframework.data.domain.PageImpl: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: java.io.PushbackInputStream@6e88a853; line: 3, column: 5] (through reference chain: com.sysware.cloud.plugin.bean.Response["data"]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1206) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:314) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2938) ~[jackson-databind-2.8.10.jar:2.8.10] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:96) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.cloud.netflix.feign.support.SpringDecoder.decode(SpringDecoder.java:59) ~[spring-cloud-netflix-core-1.4.2.RELEASE.jar:1.4.2.RELEASE] at org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:45) ~[spring-cloud-netflix-core-1.4.2.RELEASE.jar:1.4.2.RELEASE] at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:165) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:133) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.5.0.jar:?] at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.5.0.jar:?] at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) ~[hystrix-core-1.5.12.jar:1.5.12] at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) ~[hystrix-core-1.5.12.jar:1.5.12] at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.2.0.jar:1.2.0] ... 26 more
大概意思是說:不能構造實例 org.springframework.data.domain.PageImpl : 沒有合適的構造函數(此處還在查資料jackson反序列化和構造函數的關系),然后查閱PageImpl的源碼,發現確實沒有無參的構造函數
public PageImpl(List<T> content, Pageable pageable, long total) { super(content, pageable); this.pageable = pageable; this.total = !content.isEmpty() && pageable != null && (long)(pageable.getOffset() + pageable.getPageSize()) > total?(long)(pageable.getOffset() + content.size()):total; } public PageImpl(List<T> content) { this(content, (Pageable)null, null == content?0L:(long)content.size()); }
然后我們通過自定義類繼承PageImpl ,並添加無參構造函數,代碼如下(這是最終版,其實剛開始的時候Sort上沒有加@JsonDeserialize(using=CustomSortDeserializer.class)),后面說加這個注解的作用。
package com.sysware.cloud.plugin.commons.http; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Data; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import java.util.ArrayList; import java.util.List; /** * Created by tianwenqing on 2018/7/9. */ @Data public class RestResponsePage<T> extends PageImpl<T> { public RestResponsePage(List<T> content, Pageable pageable, long total) { super(content, pageable, total); } private int number; private int size; private int totalPages; private int numberOfElements; private long totalElements; private boolean previousPage; private boolean first; private boolean nextPage; private boolean last; private List<T> content; @JsonDeserialize(using=CustomSortDeserializer.class) private Sort sort; public RestResponsePage(List<T> content) { super(content); } public RestResponsePage() { super(new ArrayList<T>()); } public PageImpl<T> pageImpl() { return new PageImpl<T>(getContent(), new PageRequest(getNumber(), getSize(), getSort()), getTotalElements()); } }
package com.sysware.cloud.plugin.commons.http; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import org.springframework.data.domain.Sort; import java.io.IOException; /** * Created by tianwenqing on 2018/7/9. */ public class CustomSortDeserializer extends JsonDeserializer<Sort> { @Override public Sort deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { ArrayNode node = jp.getCodec().readTree(jp); Sort.Order[] orders = new Sort.Order[node.size()]; int i = 0; for(JsonNode obj : node){ orders[i] = new Sort.Order(Sort.Direction.valueOf(obj.get("direction").asText()), obj.get("property").asText()); i++; } Sort sort = new Sort(orders); return sort; } }
上面的的兩個類就是最終代碼,然后FeginClient端接受的時候用RestResponsePage替代Page,即可。
下面介紹@JsonDeserialize(using=CustomSortDeserializer.class)
剛開始創建完RestResponsePage類,沒有加@JsonDeserialize(using=CustomSortDeserializer.class) 直接引用,結果又報下面的錯:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of org.springframework.data.domain.Sort out of START_ARRAY token at [Source: java.io.PushbackInputStream@5f88be0f; line: 84, column: 14] (through reference chain: com.sysware.cloud.plugin.bean.Response["data"]->com.sysware.cloud.test.aggregation.RestResponsePage["sort"]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1122) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1075) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromArray(BeanDeserializerBase.java:1370) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:174) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:357) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2938) ~[jackson-databind-2.8.10.jar:2.8.10] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:96) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.cloud.netflix.feign.support.SpringDecoder.decode(SpringDecoder.java:59) ~[spring-cloud-netflix-core-1.4.2.RELEASE.jar:1.4.2.RELEASE] at org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:45) ~[spring-cloud-netflix-core-1.4.2.RELEASE.jar:1.4.2.RELEASE] at feign.SynchronousMethodHandler.decode(SynchronousMethodHandler.java:165) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:133) ~[feign-core-9.5.0.jar:?] at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.5.0.jar:?] at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.5.0.jar:?] at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) ~[hystrix-core-1.5.12.jar:1.5.12] at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) ~[hystrix-core-1.5.12.jar:1.5.12] at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.2.0.jar:1.2.0] ... 26 more
大概意思是說不能反序列化Sort屬性,所以我們使用自定義反序列化器序列化Sort,至於為什么不能反序列化,google找到如下解釋,還沒有看太懂
If you look at Sort, you'll see that it implements Iteratable for Order. And if you examine Order defined in the same class, you'll see the fields in my example JSON. So when Sort is serialized, it is serialized as a list of Order, which is why deserializing into Sort needs a custom deserializer implementation, and also why deserializing without a custom implementation fails (because it's trying to deserialize an array into a single object).
During deserialization, when a setter annotated with @JsonDeserializer is encountered as each property is deserialized, Jackson uses the supplied deserializer to deserialize that specific property.
