Spring中Jackson的JsonNode字段如何简洁设置默认值?
更简洁的实现方案
针对你遇到的问题,我有几个更优雅的解决方案,既能避免重复代码,又能满足info为null时getInfo().toString()返回"{}"的需求:
方案1:全局Jackson反序列化配置(推荐,无侵入)
如果你的场景主要是JSON反序列化时处理null值,推荐使用自定义Jackson反序列化器,全局生效,不需要修改任何DTO的代码:
- 首先创建一个自定义反序列化器,将null的JsonNode替换为空的ObjectNode:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import java.io.IOException; public class NullToEmptyObjectNodeDeserializer extends JsonDeserializer<JsonNode> { @Override public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.readValueAsTree(); return node == null ? JsonNodeFactory.instance.objectNode() : node; } }
- 在Spring Boot中注册这个反序列化器到ObjectMapper:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 为JsonNode类型注册自定义反序列化器 module.addDeserializer(JsonNode.class, new NullToEmptyObjectNodeDeserializer()); objectMapper.registerModule(module); return objectMapper; } }
这样所有JsonNode类型的字段在反序列化时,如果原JSON中没有该字段或者值为null,都会被自动设置为空的ObjectNode,后续调用getInfo().toString()自然返回"{}"。如果需要覆盖手动设置null的场景,可以配合下面的方案2补充处理。
方案2:通用工具方法+Lombok简化
如果你需要覆盖手动设置null的场景,可以提取一个通用工具方法,结合Lombok避免重复编写setter/getter:
- 创建工具类:
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; public class JsonNodeUtils { public static JsonNode emptyIfNull(JsonNode node) { return node == null ? JsonNodeFactory.instance.objectNode() : node; } }
- 在DTO中使用自定义setter/getter(Lombok会识别自定义方法,不会生成默认实现):
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; @Data class ABC { @JsonProperty("info") private JsonNode info; // 自定义setter,自动处理null值 public void setInfo(JsonNode info) { this.info = JsonNodeUtils.emptyIfNull(info); } // 双重保险:即使字段被直接赋值null,getter也返回空节点 public JsonNode getInfo() { return JsonNodeUtils.emptyIfNull(this.info); } }
这样不管是反序列化、手动调用setter,还是直接给字段赋值null,getInfo()都会返回空的ObjectNode,完全满足需求。工具类的方法可以在多个DTO中复用,无需重复编写判断逻辑。
方案3:抽象父类复用逻辑
如果有多个DTO都包含类似的JsonNode字段,可以创建一个抽象父类,把getter/setter的逻辑抽离进去,子类直接继承即可:
import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; public abstract class BaseJsonNodeDto { protected JsonNode info; @JsonSetter("info") public void setInfo(JsonNode info) { this.info = info == null ? JsonNodeFactory.instance.objectNode() : info; } public JsonNode getInfo() { return this.info; } // 可以添加更多类似JsonNode字段的通用处理逻辑 }
然后你的DTO继承这个父类:
import lombok.Data; @Data class ABC extends BaseJsonNodeDto { // 其他字段... }
这种方式适合多个DTO有相同字段的场景,彻底消除重复代码。
补充说明
你提到的@JsonProperty(defaultValue = "{}")无效是正常的——这个注解的defaultValue属性仅用于生成API文档(比如Swagger),不会实际为字段设置默认值,所以无法解决你的问题。
内容的提问来源于stack exchange,提问作者comiventor
相关产品推荐
相关产品推荐

