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?
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;
}