我在尝试反序列化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"
}
和execption:
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)
编辑:添加了一些代码示例
让我们说这是我的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;
}
很棒,它不需要改变其他任何东西(在我的mapper类中)。但假设我得到了这种json
{
"firstName": "name"
}
现在它抛出了NPE。因此,我们的想法是检查mapper中的值是否为null。我们来做吧:
@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;
}
问题是,在这种情况下,我必须更改我的mapper类,因为第4个参数未被处理。
答案 0 :(得分:1)
此行返回null
node.get("id") // returns null when property is not defined
因为该属性显然未在JSON中定义。您可以通过多种方式解决问题,但在使用get(name)
和其中一个方法asLong,asString等时,您将始终必须检查null,或者只检查属性是否使用has(name)
定义。
您还可以使用辅助功能
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"));
编辑(根据编辑过的问题):
配置对象映射器时,您可以使用configure方法指定它应该有多严格,可以使用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中可以看到的使用final属性,当你使用它们时,你必须创建一个带参数的构造函数,并且不能创建一个空的构造函数(至少事先没有指定它的值) ,这个空构造函数对于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