Spring 4.2.3 y fasterxml Jackson 2.7.0 son incompatibles

Después de la migración de fasterxml.jackson 2.6.3 a 2.7.0. Esto se debe a que se public JavaType constructType(Type type, Class contextType) utilizado en Spring’s AbstractJackson2HttpMessageConverter . ¿Cómo arreglar esto? Estoy usando Spring 4.2.3.

 /signin/facebook java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.type.TypeFactory.constructType(Ljava/lang/reflect/Type;Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.getJavaType(AbstractJackson2HttpMessageConverter.java:314) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.canRead(AbstractJackson2HttpMessageConverter.java:146) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.canRead(AbstractJackson2HttpMessageConverter.java:141) at org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback.doWithRequest(RestTemplate.java:706) at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:770) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:594) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557) at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:357) at org.springframework.social.oauth2.OAuth2Template.postForAccessGrant(OAuth2Template.java:242) at org.springframework.social.oauth2.OAuth2Template.exchangeForAccess(OAuth2Template.java:144) at org.springframework.social.connect.web.ConnectSupport.completeConnection(ConnectSupport.java:160) at org.springframework.social.connect.web.ProviderSignInController.oauth2Callback(ProviderSignInController.java:228) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:44) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) 

El soporte para Jackson 2.7 se agregará en la spring 4.3. Ver https://jira.spring.io/browse/SPR-13483 .

Por ahora no puede usarlo sin proporcionar sus propias clases de integración.

Las mejores versiones compatibles

1) Spring 4.2.4 funciona con fasterxml Jackson 2.7.2 o 2.8.4

2) Spring 4.3.5 funciona con fasterxml Jackson 2.7.0

Necesitaba reemplazar MappingJackson2HttpMessageConverter para que todo funcionara (principalmente generics). De hecho, acaba de MappingJackson2HttpMessageConverter del maestro Spring MVC 4.3:

 public class MappingJackson27HttpMessageConverter extends MappingJackson2HttpMessageConverter { public MappingJackson27HttpMessageConverter() { } public MappingJackson27HttpMessageConverter(ObjectMapper objectMapper) { super(objectMapper); } @Override public boolean canRead(Type type, Class contextClass, MediaType mediaType) { JavaType javaType = getJavaType(type, contextClass); if (!logger.isWarnEnabled()) { return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType)); } AtomicReference causeRef = new AtomicReference(); if (this.objectMapper.canDeserialize(javaType, causeRef) && canRead(mediaType)) { return true; } Throwable cause = causeRef.get(); if (cause != null) { String msg = "Failed to evaluate deserialization for type " + javaType; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } else { logger.warn(msg + ": " + cause); } } return false; } @Override public boolean canWrite(Class clazz, MediaType mediaType) { if (!logger.isWarnEnabled()) { return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType)); } AtomicReference causeRef = new AtomicReference(); if (this.objectMapper.canSerialize(clazz, causeRef) && canWrite(mediaType)) { return true; } Throwable cause = causeRef.get(); if (cause != null) { String msg = "Failed to evaluate serialization for type [" + clazz + "]"; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } else { logger.warn(msg + ": " + cause); } } return false; } @Override protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType()); JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding); try { writePrefix(generator, object); Class serializationView = null; FilterProvider filters = null; Object value = object; JavaType javaType = null; if (object instanceof MappingJacksonValue) { MappingJacksonValue container = (MappingJacksonValue) object; value = container.getValue(); serializationView = container.getSerializationView(); filters = container.getFilters(); } if (type != null && value != null && TypeUtils.isAssignable(type, value.getClass())) { javaType = getJavaType(type, null); } ObjectWriter objectWriter; if (serializationView != null) { objectWriter = this.objectMapper.writerWithView(serializationView); } else if (filters != null) { objectWriter = this.objectMapper.writer(filters); } else { objectWriter = this.objectMapper.writer(); } if (javaType != null && javaType.isContainerType()) { objectWriter = objectWriter.forType(javaType); } objectWriter.writeValue(generator, value); writeSuffix(generator, object); generator.flush(); } catch (JsonProcessingException ex) { throw new HttpMessageNotWritableException("Could not write content: " + ex.getMessage(), ex); } } /** * Return the Jackson {@link JavaType} for the specified type and context * class. * 

* The default implementation returns * {@code typeFactory.constructType(type, contextClass)}, but this can be * overridden in subclasses, to allow for custom generic collection * handling. For instance: *

 * protected JavaType getJavaType(Type type) { if (type instanceof Class && * List.class.isAssignableFrom((Class)type)) { return * TypeFactory.collectionType(ArrayList.class, MyBean.class); } else { * return super.getJavaType(type); } } * 

* * @param type the generic type to return the Jackson JavaType for * @param contextClass a context class for the target type, for example a * class in which the target type appears in a method signature (can be * {@code null}) * @return the Jackson JavaType */ @Override protected JavaType getJavaType(Type type, Class contextClass) { TypeFactory typeFactory = this.objectMapper.getTypeFactory(); if (type instanceof TypeVariable && contextClass != null) { ResolvableType resolvedType = resolveVariable( (TypeVariable) type, ResolvableType.forClass(contextClass)); if (resolvedType != ResolvableType.NONE) { return typeFactory.constructType(resolvedType.resolve()); } } return typeFactory.constructType(type); } private ResolvableType resolveVariable(TypeVariable typeVariable, ResolvableType contextType) { ResolvableType resolvedType; if (contextType.hasGenerics()) { resolvedType = ResolvableType.forType(typeVariable, contextType); if (resolvedType.resolve() != null) { return resolvedType; } } resolvedType = resolveVariable(typeVariable, contextType.getSuperType()); if (resolvedType.resolve() != null) { return resolvedType; } for (ResolvableType ifc : contextType.getInterfaces()) { resolvedType = resolveVariable(typeVariable, ifc); if (resolvedType.resolve() != null) { return resolvedType; } } return ResolvableType.NONE; } }

Intereting Posts