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

SpringBoot返回List<UUID>时触发HttpMessageNotWritableException问题

问题分析与解决

核心原因:Java泛型类型擦除

你遇到的问题本质是泛型类型擦除导致的。Java泛型是编译期特性,运行时所有泛型参数会被擦除为其上限类型(通常是Object)。你的UUIDListConvertor继承自泛型JsonConverter<List<UUID>>,但运行时JsonConverter中的泛型参数T会被擦除,无法保留List<UUID>的具体类型信息。

当Spring的Jackson消息转换器在序列化响应中的List<UUID>时,由于无法识别列表元素的实际类型是UUID,会默认按String类型处理,进而触发String无法转换为UUID的类型转换异常。

而手动创建List<UUID>返回时,方法的返回类型或DTO字段的泛型信息在编译期已明确,Jackson可以通过反射获取到List<UUID>的具体类型,因此能正确序列化。

为何手动指定TypeReference<List<UUID>>能解决问题

TypeReference是Jackson提供的工具类,它通过匿名内部类的方式保留了泛型类型信息(JVM会在匿名内部类的字节码中记录泛型参数)。当你手动指定TypeReference<List<UUID>>时,Jackson可以明确知道要处理的是List<UUID>类型,从而正确序列化/反序列化每个UUID元素。

可行解决方案

方案1:改进自定义转换器,明确指定TypeReference

修改UUIDListConvertor,直接在子类中定义具体的TypeReference,绕过泛型擦除的限制:

public class UUIDListConvertor extends JsonConverter<List<UUID>> {
    // 明确指定List<UUID>的TypeReference
    private static final TypeReference<List<UUID>> UUID_LIST_TYPE = new TypeReference<>() {};
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(List<UUID> attribute) {
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to serialize UUID list", e);
        }
    }

    @Override
    public List<UUID> convertToEntityAttribute(String dbData) {
        try {
            return objectMapper.readValue(dbData, UUID_LIST_TYPE);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Failed to deserialize UUID list", e);
        }
    }
}

方案2:在DTO字段上明确指定序列化器

如果不想修改转换器,可以在DTO的List<UUID>字段上添加Jackson注解,指定元素的序列化/反序列化器:

public class OrderRequestDTO {
    @JsonSerialize(contentUsing = UUIDSerializer.class)
    @JsonDeserialize(contentUsing = UUIDDeserializer.class)
    private List<UUID> items;

    // getter/setter
}

Jackson自带UUIDSerializer和UUIDDeserializer,可以直接使用,确保元素按UUID类型处理。

方案3:为泛型转换器添加类型信息获取逻辑

如果需要保留泛型JsonConverter的通用性,可以在父类中通过反射获取子类的泛型参数类型:

public abstract class JsonConverter<T> implements AttributeConverter<T, String> {
    protected final ObjectMapper objectMapper = new ObjectMapper();
    protected final TypeReference<T> typeReference;

    @SuppressWarnings("unchecked")
    public JsonConverter() {
        // 通过反射获取子类的泛型参数类型
        Type superType = getClass().getGenericSuperclass();
        if (superType instanceof ParameterizedType parameterizedType) {
            Type actualType = parameterizedType.getActualTypeArguments()[0];
            this.typeReference = new TypeReference<T>() {
                @Override
                public Type getType() {
                    return actualType;
                }
            };
        } else {
            throw new IllegalArgumentException("Generic supertype required");
        }
    }

    @Override
    public String convertToDatabaseColumn(T attribute) {
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Serialize failed", e);
        }
    }

    @Override
    public T convertToEntityAttribute(String dbData) {
        try {
            return objectMapper.readValue(dbData, typeReference);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Deserialize failed", e);
        }
    }
}

这样UUIDListConvertor继承后,父类就能通过反射获取到List<UUID>的类型信息,无需手动指定TypeReference。

内容的提问来源于stack exchange,提问作者XProtocol

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:13:17