我在尝试反序列化 JSON 时仍然遇到 NPE。如果属性丢失,我希望设置默认值(0/null)。这是我的 Spring Boot 配置 bean:
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
jsonConverter.setObjectMapper(objectMapper());
return jsonConverter;
}
ObjectMapper objectMapper() {
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(ProductBasicDto.class, new ProductDeserializer());
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
mapper.registerModule(simpleModule);
return mapper;
}
我的自定义解串器:
@Override
public ProductBasicDtoWrapper deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
System.out.println(node.get("id").asLong()); // here it throws NPE
return null; // ignore that, just for testing
}
json:
{
"name": "js",
"category": "CoNiFer"
}
和执行:
java.lang.NullPointerException: null
at api.product.infrastructure.ProductDeserializer.deserialize(ProductDeserializer.java:19)
at api.product.infrastructure.ProductDeserializer.deserialize(ProductDeserializer.java:14)
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)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:206)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:157)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:870)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:776)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:881)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
- 如何避免此 NPE?
- 如果属性(如本例中的 id)丢失,是否可以在解串器中不进行显式检查的情况下放入 0/null 值?
编辑:添加了一些代码示例
假设这是我的 DTO 类:
class MyDto implements Serializable {
private final String firstName;
private final String lastName;
}
现在我正在创建我的自定义映射器:
@Override
public ProductBasicDtoWrapper deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException,
JsonProcessingException {
objectMapper.convertValue(jsonNode, MyDto.class);
...
现在我决定向 MyDto 添加额外的 Integer:
class MyDto implements Serializable {
private final String firstName;
private final String lastName;
private final Integer age;
}
这很棒,不需要更改任何其他内容(在我的映射器类中)。但假设我得到了这种 json
{
"firstName": "name"
}
现在它会抛出 NPE。所以想法是检查映射器中的值是否为空。让我们开始吧:
@Override
public ProductBasicDtoWrapper deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException,
JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
node.get("firstName") == null ?
node.get("lastName") == null ?
node.get("age") == null ?
}
好吧,现在可以了,现在假设我向 DTO 类添加了一个属性
class MyDto implements Serializable {
private final String firstName;
private final String lastName;
private final Integer age;
private final String card;
}
问题是,在这种情况下,我还必须更改我的映射器类,因为未处理第四个参数。
最佳答案
该行返回 null
node.get("id") // returns null when property is not defined
因为该属性显然没有在 JSON 中定义。您可以通过多种方式解决您的问题,但在使用 get(name) 以及 asLong、asString 等方法之一时,您始终必须检查 null,或者仅检查该属性是否定义为有(名称)
。
您还可以使用辅助函数
public static Long getLong(JsonNode node, String name) {
if (node.get(name) != null && node.get(name).isLong()) {
return node.get(name).asLong();
} else {
return null;
}
}
你可以返回null,或者抛出异常,如果返回null你应该小心,稍后再处理。
然后用它来打印变量或未定义时为 null。
System.out.println(getLong(node, "id"));
编辑(根据编辑的问题):
当您配置对象映射器时,您可以使用配置方法指定它的严格程度,您可以使用 DeserializationFeature 枚举来指示它何时应该失败以及何时不应该失败。
在这里您可以看到每个功能及其用途: https://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/DeserializationFeature.html
现在,如果类中的属性名称和 json 匹配,您可以将 json 转换为 dto 对象,如下所示:
ObjectMapper mapper = new ObjectMapper() // object mapper with wanted properties
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,
mapper.readValue(json, MyDto.class);
这里您只需要创建映射器(不需要反序列化器),然后将其转换为目标类。
如果类和 json 中有不同的名称,则必须使用注释 @JsonProperty 并指定名称。
可能出现的问题:
现在,我在 DTO 中看到您使用了最终属性,当您使用它们时,您必须创建一个带参数的构造函数,并且不能创建一个空构造函数(至少没有事先指定它的值),这个空构造函数对于 java POJO 是必需的,并且由对象映射器在内部使用。
所以你的 DTO 应该是这样的:
class MyDto implements Serializable {
private String firstName;
private String lastName;
private Integer age;
// empty constructor (only necessary when another constructor is specified)
// getters and setters
}
如果您仍然需要使用 Immutable 对象而不是 POJO,您可以创建如下所示的类:
class MyDto implements Serializable {
private final String firstName;
private final String lastName;
private final Integer age;
@JsonCreator
MyDto(@JsonProperty("firstName") String firstName,
@JsonProperty("lastName") String lastName,
@JsonProperty("age") Integer age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
// getters
}
使用映射器
使用上面的 POJO 类和对象映射器,您可以执行以下操作:
MyDto myDto1 = mapper.readValue("{\"firstName\": \"name\"}", MyDto.class); // object with firstName defined, lastName and age are null
MyDto myDto2 = mapper.readValue("{\"firstName\": \"name\",\"lastName\": \"last\"}", MyDto.class); // object with firstName and lastName defined, age is null
MyDto myDto3 = mapper.readValue("{\"firstName\": \"name\",\"lastName\": \"last\",\"age\": 1}", MyDto.class); // object with firstName, lastName and age defined
您甚至可以使用空对象或具有未知属性。
MyDto myDto4 = mapper.readValue("{}", MyDto.class); // object with all properties null
MyDto myDto5 = mapper.readValue("{\"blablah\": \"name\"}", MyDto.class); // object with all properties null
关于java - Jackson 在对丢失的属性(property)进行反序列化期间抛出 NPE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49070317/