You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止Jackson因客户端传_links:null将RepresentationModel的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 03:36:20