即使使用 NON_NULL,Jackson ObjectMapper 也会抛出 NullPointerException

Posted

技术标签:

【中文标题】即使使用 NON_NULL,Jackson ObjectMapper 也会抛出 NullPointerException【英文标题】:Jackson ObjectMapper throwing NullPointerException even with NON_NULL 【发布时间】:2016-08-13 18:47:46 【问题描述】:

当使用以下 JSON 并且“phones”或“emailAddresses”为空时,我会收到 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": []

这是堆栈跟踪:

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

我正在像这样设置我的自定义 ObjectMapper:

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();
    

这意味着CteUserContext cteUserContext = MigrationObjectMapper.getMigrationObjectMapper().convertValue(item.getItem(), new TypeReference<CteUserContext>()); 正在抛出错误。

CteUserContext里面是一个User对象,里面包含List和List。这些难道不应该根据对象映射器配置进行序列化吗?

【问题讨论】:

Caused by: java.lang.NullPointerException at com.test.User.setPhones(User.java:202) @Rocki 但是,当映射器配置为忽略 NULL 值时,为什么还要尝试使用设置器呢?它不考虑等级较低的类吗? setSerializationInclusion 只会忽略 serialization 而不是 deserialization 的 null 值。您可以让您的用户模型知道空值或使用自定义JsonNodeDeserializer 并覆盖public JsonNode getNullValue() 【参考方案1】:

根据 Rocki 的评论:

setSerializationInclusion 只会忽略空值 序列化不是反序列化。

如果传入的“电话”值在我的用户类中为空,我没有处理,所以它正在抛出 NPE。我已经更新以确认它不为空。如果是,它会在任何其他逻辑之前短路:

/**
 * @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;
 

【讨论】:

我记得仔细阅读了堆栈跟踪。谢谢。【参考方案2】:

我想知道 phones 属性是否实际存在于您的 JSON 中,这是否意味着您的映射器没有将其视为 NON_NULL (即它没有丢失,它存在但您给它的值是 @ 987654322@ 明确)

尝试改用JsonInclude.Include.NON_DEFAULT

如果失败了,您是否尝试过在类上使用注释而不是 this.setSerializationInclusion()?

@JsonInclude(Include.NON_DEFAULT)
public class MigrationObjectMapper 
    ....

【讨论】:

以上是关于即使使用 NON_NULL,Jackson ObjectMapper 也会抛出 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

Jackson 包含 non_null 不能处理来自 kotlin 扩展函数的对象

@JsonInclude(Include.NON_NULL)

@JsonInclude(Include.NON_NULL)

jackson 处理空值

Jackson Java 到 JSON 对象映射器修改字段名称

jackSon注解– @JsonInclude 注解不返回null值字段