8

I'm using spring-mvc for a restful service. By adding the dependency, my rest controllers automatically generate json from my response entities.

Dependency

jackson = '2.9.0'
compile "com.fasterxml.jackson.core:jackson-databind:$jackson"

Objects

@Getter @Setter
public final class UserDTO {
    [...]

    private int id;
    private Set<ActivityDTO> activities = Sets.newHashSet();
}


@Getter @Setter
public final class ActivityDTO {
    [...]

    private int id;
    private Set<ActivityDTO> subActivities;
}

Problem

Problems come in special cases. For example, I want to add an activity to an user. The following user-json works good, Jackson converts it without error.

{
"id": 1,
  "activities": [
    {
      "id": 1,
      "subActivities": null,
    }
  ]
}

But if I add an activity with sub activities, it crashes!

{
  "id": 6,
  "activities": [
    {
      "id": 19,
      "subActivities": [
        {
          "id": 20,
          "subActivities": [
            {
              "id": 24,
              "subActivities": null
            },
            {
              "id": 21,
              "subActivities": [
                {
                  "id": 23,
                  "subActivities": null
                },
                {
                  "id": 22,
                  "subActivities": null
                }
              ]
            }
          ]
        }
      ],
    }
  ]
}

Indeed, jackson can't deserialize this json and I don't understand why. I get the following error.. Any idea?

13:45:56.890 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod - Failed to resolve argument 0 of type 'com.ortec.gta.database.model.dto.UserDTO'
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: (was java.lang.NullPointerException); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.ortec.gta.database.model.dto.UserDTO["activities"]->java.util.HashSet[2]->com.ortec.gta.database.model.dto.ActivityDTO["subActivities"]->java.util.HashSet[0]->com.ortec.gta.database.model.dto.ActivityDTO["subActivities"])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:238)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223)
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:201)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:150)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:128)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:664)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.ortec.gta.configuration.ServletInitializer$CorsFilter.doFilter(ServletInitializer.java:78)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.ortec.gta.database.model.dto.UserDTO["activities"]->java.util.HashSet[2]->com.ortec.gta.database.model.dto.ActivityDTO["subActivities"]->java.util.HashSet[0]->com.ortec.gta.database.model.dto.ActivityDTO["subActivities"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:391)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1689)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:370)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:285)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:368)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:285)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:368)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235)
    ... 77 common frames omitted
Caused by: java.lang.NullPointerException: null
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:271)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:368)
    ... 93 common frames omitted

Here the objects with all fields, maybe related to my error?

@Getter @Setter
public final class ActivityDTO {
    private int id;
    private String name;
    private long creationDate;
    private long modificationDate;
    private long deletionDate;
    @JsonIgnoreProperties(value = {"activities"})
    private UserDTO lastEditor;
    @JsonIgnoreProperties(value = {"subActivities", "users", "parentActivity"})
    private ActivityDTO parentActivity;
    @JsonIgnoreProperties(value = {"parentActivity"})
    private Set<ActivityDTO> subActivities;
    @JsonIgnoreProperties(value = {"activities"})
    private Set<UserDTO> users;
    private String code;
    private boolean active;
}

@Getter @Setter
public final class UserDTO {
    private int id;
    private String username;
    private String name;
    private String lastname;
    private byte[] avatar;
    private String email;
    @JsonIgnoreProperties(value = {"activities", "activities"})
    private UserDTO superior;
    private Set<RoleDTO> roles;
    @JsonIgnoreProperties(value = {"users", "parentActivity"})
    private Set<ActivityDTO> activities;
}

@Getter @Setter
public final class RoleDTO {
    private Integer id;
    private String name;
}
  • Your problem is not reproducible with given classes and json. It is indeed falling on that ',' and works fine without it. – chimmi Aug 28 '17 at 12:32
  • it doesn't for me... –  Aug 28 '17 at 12:40
  • @chimmi edited my subject with full objects –  Aug 28 '17 at 12:41
  • @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); return objectMapper; } add thi bean to your config file and add @JsonIgnoreProperties(ignoreUnknown=true) to your pojo classes; – boukobba adil Aug 28 '17 at 12:44
  • @boukobbaadil lemme try ;) –  Aug 28 '17 at 12:45

5 Answers5

4

First of all, this definetely looks like a bug in Jackson since error only appears in version 2.8.0 and later.

As to the source of the error, it appears that the reason for it is your configuration for ActivityDTO, specifically this annotation:

@JsonIgnoreProperties(value = {"parentActivity"})
private Set<ActivityDTO> subActivities;

So you have 3 options:

  1. Rollback to Jackson 2.7.9
  2. Make custom deserializer for this case
  3. Remove this annotation and use custom serializer (if needed)
chimmi
  • 2,054
  • 16
  • 30
1

When I found this error it was because I was using primitive types as you

Try using nullable properties instead

@Getter @Setter    
public final class ActivityDTO {
    private Integer id;
    private String name;
    private Long creationDate;
    private Long modificationDate;
    private Long deletionDate;
0

It is because you have an extra "," character, which makes your json invalid. It's towards the end of your json : ],. Remove the "," and it should work.

Andrei
  • 1,613
  • 3
  • 16
  • 36
  • lemme try it now –  Aug 28 '17 at 11:54
  • I took the JSON from your post. I used an online formatter such as https://jsonformatter.curiousconcept.com/ In its current shape, it throws a parsing error (so I expect it is normal to see the error that you're seeing). If I remove the ",", then it parses OK. Can you please make sure you're deleting the correct ","? (fourth non-whitespace char from the end of your JSON). Also, the error remains the same, or changes? – Andrei Aug 28 '17 at 11:59
  • I edited my json for remove extra data, the original json has no problem; https://gist.github.com/Romain-P/d4e81ee1d272b7f56ba7db6c730c7b4b –  Aug 28 '17 at 12:01
0
@Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper;
    }

add this bean to your config file and add

@JsonIgnoreProperties(ignoreUnknown=true)

to your pojo classes;

  • It didn't fix the problem.. + if jackson is imported in dependecies, spring automatically create the ObjectMapper + some configuration. Your annotation did not help too! Thanks anyway –  Aug 28 '17 at 12:59
  • 1
    what if that pojo class is coming from third party library and worst if its not POJO? – stallion Dec 10 '20 at 13:27
  • 1
    what if that pojo is from third party library and we cannot add `JsonIgnoreProperties` annotation to that? – stallion Dec 10 '20 at 13:31
0

I don't think it's a bug in Jackson. In my case, adding allowSetters = true on @JsonIgnoreProperties on all the cascaded classes using the annotation helped fix this issue.

Dejazmach
  • 742
  • 8
  • 15