Jackson反序列化JSON为Java POJO时出现NullPointerException问题
问题根因
空指针和解析错位是多个代码错误共同导致的:
- 反序列化逻辑和实际JSON结构完全不匹配。待解析的JSON层级为
根节点 -> properties -> anon -> 路径key(/brief/versicherungsnehmer/vorname等),不存在直接命名为vorname/nachname的子节点。现有反序列化代码直接在当前节点取这两个key,拿到null后调用booleanValue()直接触发空指针。 - 类职责设计混乱。
Properties作为存储数据的POJO,错误继承了JsonDeserializer还自行实现反序列化逻辑;ProfileDeserializer类上的@JsonDeserialize(using = Properties.class)注解写反位置——该注解应该标注在POJO上指定反序列化器,写在反序列化器类上会导致Jackson反序列化逻辑错乱。 - 输入流被破坏。调试代码
is.read()提前从文件输入流读走1个字节,后续Jackson解析时拿到的是残缺的JSON内容,这也是日志中打印出dokumenttyp、sprache等无关节点的核心原因之一,流内容错位后解析逻辑完全跑偏。 - ObjectMapper重复创建、配置冗余。类初始化时已经创建了全局ObjectMapper,
readConfig方法内又重复创建实例、重复注册模块,容易引发配置冲突。 unpackNested方法泛型定义完全错误:anon节点的key是字符串路径、value是布尔值,定义为Map<Boolean, Object>后强转嵌套Map取值的逻辑本身就不可能运行成功。
修复步骤
- 修正POJO类
Properties:去掉错误的继承关系和多余的反序列化、嵌套解析方法,保留纯数据结构:import com.fasterxml.jackson.annotation.JsonProperty; public class Properties { private boolean vorname; private boolean nachname; public void setVorname(boolean vorname) { this.vorname = vorname; } public void setNachname(boolean nachname) { this.nachname = nachname; } public boolean getVorname() { return this.vorname; } public boolean getNachname() { return this.nachname; } } - 修正自定义反序列化器
ProfileDeserializer:去掉错误注解,按照实际JSON层级逐层读取节点,用安全的取值方式避免空指针:import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonDeserializer; import java.io.IOException; public class ProfileDeserializer extends JsonDeserializer<Properties> { @Override public Properties deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); Properties property = new Properties(); // 按实际JSON路径取值,path方法在节点不存在时返回空节点,不会触发空指针 JsonNode anonNode = rootNode.path("properties").path("anon"); property.setVorname(anonNode.path("/brief/versicherungsnehmer/vorname").asBoolean(false)); property.setNachname(anonNode.path("/brief/versicherungsnehmer/nachname").asBoolean(false)); return property; } } - 修正
JSONMuncher逻辑:复用全局ObjectMapper,删掉提前读取输入流的调试代码,用try-with-resources安全管理流:import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.databind.module.SimpleModule; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class JSONMuncher { private final ObjectMapper objectMapper; public JSONMuncher() { this.objectMapper = createObjectMapper(); } public ObjectMapper getObjectMapper() { return objectMapper; } private static ObjectMapper createObjectMapper() { SimpleModule module = new SimpleModule() .addSerializer(Properties.class, new ProfileSerializer()) .addDeserializer(Properties.class, new ProfileDeserializer()); return new ObjectMapper(new JsonFactory() .disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)) .registerModule(module) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(SerializationFeature.AUTO_CLOSE_SOURCE, true) .setSerializationInclusion(JsonInclude.Include.NON_NULL) .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); } public Properties readConfig(String filepath) { try (InputStream is = new FileInputStream(filepath)) { Properties anon = objectMapper.readValue(is, Properties.class); System.out.println(anon.getVorname()); System.out.println(anon.getNachname()); return anon; } catch (IOException e) { System.out.println("Beep Boop Err!!! 🤖"); e.printStackTrace(); } return null; } } - 额外校验:确认传入
readConfig的文件路径是目标JSON文件的路径,避免读到其他业务配置文件引发解析错位。
内容的提问来源于stack exchange,提问作者MagicBytes
相关产品推荐
相关产品推荐

