Jackson ObjectMapper throwing NullPointerException

2019-06-19 03:22发布

When the following JSON is used and either "phones" or "emailAddresses" are null, I'm getting a NullPointerException.

JSON:

{
  "item": {
    "messages": {
      "user.phone.missing": {
        "type": "warning",
        "key": "user.phone.missing",
        "message": "User profile does not have a phone number",
        "code": null
      },
      "user.email.missing": {
        "type": "warning",
        "key": "user.email.missing",
        "message": "User profile does not have an email address",
        "code": null
      },
      "user.es.sync.failed": {
        "type": "error",
        "key": "user.es.sync.failed",
        "message": "Unable to sync user",
        "code": null
      }
    },
    "user": {
      "firstName": "Test",
      "middleInitial": null,
      "lastName": "User",
      "createdDt": "2016-04-20 19:50:03+0000",
      "updatedDt": null,
      "lastVerifiedDt": null,
      "status": "DEACTIVATED",
      "tokens": [
        {
          "tokenHash": "test hash",
          "tokenValue": "test dn",
          "createdDt": "2016-04-20 19:50:03+0000",
          "updatedDt": null,
          "status": "ENABLED"
        }
      ],
      "phones": null,
      "emailAddresses": null
    }
  },
  "status": "SUCCESS",
  "errors": []
}

And here's the stacktrace:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: N/A (through reference chain: com.test.message.cte.CteItemResponse["item"]->com.test.message.cte.CteUserContext["user"]->com.test.User["phones"])
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:510)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:493)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.set(MethodProperty.java:116)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:295)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:464)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:295)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:464)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:295)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2888)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2041)
    at com.test.Test.main(Test.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.NullPointerException
    at com.test.User.setPhones(User.java:202)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.set(MethodProperty.java:114)
    ... 19 more

I'm setting up my custom ObjectMapper like this:

package com.test;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;

public class MigrationObjectMapper extends ObjectMapper {
    private MigrationObjectMapper() {
        // do not serialize null value fields
        this.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static MigrationObjectMapper getMigrationObjectMapper(){
        return new MigrationObjectMapper();
    }
}

Which means CteUserContext cteUserContext = MigrationObjectMapper.getMigrationObjectMapper().convertValue(item.getItem(), new TypeReference<CteUserContext>(){}); is throwing the error.

Inside CteUserContext is a User object, which contains List and List. Shouldn't these just not be serialized based on the object mapper configuration?

2条回答
Rolldiameter
2楼-- · 2019-06-19 03:28

I'm wondering whether the fact that the phones property is actually present in your JSON, means your mapper is not treating it as NON_NULL (i.e. it's not missing, it is there but you have given it a value of null explicitly)

Try using JsonInclude.Include.NON_DEFAULT instead.

Failing that, have you tried using the annotation on the class instead of this.setSerializationInclusion()?

@JsonInclude(Include.NON_DEFAULT)
public class MigrationObjectMapper {
    ....
}
查看更多
男人必须洒脱
3楼-- · 2019-06-19 03:30

Per Rocki's comment:

setSerializationInclusion will ignore null values only for serialization not deserialization.

I wasn't handling if the incoming "phones" value was null in my User class, so it was throwing the NPE. I've updated to confirm that it isn't null. If it is, it'll short circuit before any other logic:

/**
 * @param phones The phones
 */
@JsonProperty("phones")
public void setPhones(List<Phone> phones) {
    if ((phones != null) && (phones.size() > 1)) {
        int primaryIndex = -1;
        Phone primaryPhone = null;

        for (Phone p : phones) {
            if (p.getIsPrimary()) {
                primaryIndex = phones.indexOf(p);
                primaryPhone = p;
                break;
            }
        }

        phones.remove(primaryIndex);
        phones.add(0, primaryPhone);
    }
    this.phones = phones;
} 
查看更多
登录 后发表回答