如何阻止Jackson因客户端传_links:null将RepresentationModel的links设为null?
解决Spring HATEOAS客户端传入
_links: null导致的序列化异常 针对你遇到的问题,这里提供几个比反射更优雅的解决方案,从根源或修复层面处理:
1. 标记_links为反序列化只读(推荐)
无需全局配置Jackson,只需在你的领域类中重写getLinks()方法并添加Jackson的只读注解,让Jackson仅在序列化时输出链接,反序列化时忽略客户端传入的_links字段:
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; import org.springframework.hateoas.Links; import org.springframework.hateoas.RepresentationModel; public class YourDomainModel extends RepresentationModel<YourDomainModel> { // 你的业务字段 @Override @JsonProperty(access = Access.READ_ONLY) public Links getLinks() { return super.getLinks(); } }
这个方法直接切断了客户端输入对links字段的修改路径,既保留了响应中的HATEOAS链接,又避免了_links: null带来的null值问题,是最干净的解决方案。
2. 用@JsonPostConstruct修复null值
如果无法修改反序列化规则,可以在领域类中添加反序列化后的初始化方法,自动修复被设为null的links字段:
import org.springframework.hateoas.RepresentationModel; import com.fasterxml.jackson.annotation.JsonPostConstruct; import java.lang.reflect.Field; import java.util.ArrayList; public class YourDomainModel extends RepresentationModel<YourDomainModel> { // 你的业务字段 @JsonPostConstruct private void fixNullLinks() { try { Field linksField = RepresentationModel.class.getDeclaredField("links"); linksField.setAccessible(true); if (linksField.get(this) == null) { linksField.set(this, new ArrayList<>()); } linksField.setAccessible(false); } catch (Exception e) { // 可根据业务需求添加日志或异常处理 } } }
Jackson在反序列化完成后会自动调用这个方法,检查并初始化links字段,避免后续序列化时抛出异常。
3. 全局自定义反序列化器(多领域类场景)
如果有大量领域类继承RepresentationModel,可以全局注册一个自定义反序列化器,统一处理null值问题:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.hateoas.RepresentationModel; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper(ObjectMapper defaultMapper) { SimpleModule module = new SimpleModule(); // 替换为你的基础领域类(如果有)或直接针对RepresentationModel module.addDeserializer(RepresentationModel.class, new RepresentationModelDeserializer<>( RepresentationModel.class, defaultMapper.getDeserializationConfig(), defaultMapper.getDeserializationConfig().findRootValueDeserializer( defaultMapper.constructType(RepresentationModel.class) ) )); defaultMapper.registerModule(module); return defaultMapper; } static class RepresentationModelDeserializer<T extends RepresentationModel<T>> extends JsonDeserializer<T> { private final Class<T> targetClass; private final DeserializationConfig config; private final JsonDeserializer<Object> defaultDeserializer; public RepresentationModelDeserializer(Class<T> targetClass, DeserializationConfig config, JsonDeserializer<Object> defaultDeserializer) { this.targetClass = targetClass; this.config = config; this.defaultDeserializer = defaultDeserializer; } @Override @SuppressWarnings("unchecked") public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { T model = (T) defaultDeserializer.deserialize(p, ctxt); // 修复null的links字段 try { Field linksField = RepresentationModel.class.getDeclaredField("links"); linksField.setAccessible(true); if (linksField.get(model) == null) { linksField.set(model, new ArrayList<>()); } linksField.setAccessible(false); } catch (Exception e) { throw new IOException("Failed to initialize links field", e); } return model; } } }
这个方案适用于多领域类的场景,无需逐个修改类,统一处理null值问题。
4. 升级Spring HATEOAS版本
部分新版本的Spring HATEOAS已经修复了这类问题——比如在getLinks()方法中添加了null值校验,或默认在反序列化时忽略_links字段。如果你的项目依赖允许,升级到1.3.x及以上版本可能直接解决问题。
内容的提问来源于stack exchange,提问作者Horacio Conde
相关产品推荐
相关产品推荐

