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

Kafka消费者泛型JSON反序列化异常:LinkedHashMap转自定义类失败

泛型JSON反序列化问题解决方案

问题场景

我有一个Kafka消费者库,用于消费字节数组。上游始终会将JSON字符串转换为字节进行发布。消费后转换为字符串时,我需要将其反序列化为不同消费者对应的不同类。

尝试的反序列化实现:

public class MessageUtil<T> {

    public T deserializeByStringConversion(final byte[] message) throws Exception {
        String json = IOUtils.toString(message);
        return new ObjectMapper().readValue(json, new TypeReference<T>() {});
    }
}

调用方式:

MessageUtil<MyClass> messageUtil;

@BeforeClass
public void setup() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void testMyClass() throws Exception {
    MyClass myClass = messageUtil.deserializeByStringConversion(IOUtils.toByteArray(request));
    Assert.assertNotNull(myClass);
}

运行时抛出异常:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.a.b.c.d.MyClass

问题原因

Java泛型存在类型擦除机制:编译后MessageUtil<T>中的泛型参数T会被擦除为Object。此时new TypeReference<T>() {}无法获取到实际的MyClass类型信息,Jackson只能将JSON反序列化为默认的LinkedHashMap,强转时就会抛出类型转换异常。

解决方案

方案1:传入目标类的Class对象(推荐,简单直接)

修改工具类,让调用者传入要反序列化的目标类的Class实例,Jackson可以直接基于该Class完成类型匹配:

public class MessageUtil {
    // 复用ObjectMapper实例,避免重复创建开销
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    public <T> T deserializeByStringConversion(final byte[] message, Class<T> targetClass) throws Exception {
        String json = IOUtils.toString(message);
        return OBJECT_MAPPER.readValue(json, targetClass);
    }
}

调用方式:

MessageUtil messageUtil = new MessageUtil();

@Test
public void testMyClass() throws Exception {
    MyClass myClass = messageUtil.deserializeByStringConversion(IOUtils.toByteArray(request), MyClass.class);
    Assert.assertNotNull(myClass);
}

方案2:通过子类保留泛型类型信息

如果希望工具类实例绑定固定类型,可以通过创建子类的方式保留泛型参数的实际类型:

首先修改MessageUtil,在构造时解析父类的泛型参数:

public class MessageUtil<T> {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private final TypeReference<T> typeReference;

    @SuppressWarnings("unchecked")
    public MessageUtil() {
        Type superClass = getClass().getGenericSuperclass();
        if (!(superClass instanceof ParameterizedType)) {
            throw new IllegalArgumentException("MessageUtil必须继承为带具体类型的子类");
        }
        ParameterizedType parameterizedType = (ParameterizedType) superClass;
        this.typeReference = new TypeReference<T>() {} {
            @Override
            public Type getType() {
                return parameterizedType.getActualTypeArguments()[0];
            }
        };
    }

    public T deserializeByStringConversion(final byte[] message) throws Exception {
        String json = IOUtils.toString(message);
        return OBJECT_MAPPER.readValue(json, typeReference);
    }
}

创建对应类型的子类:

public class MyClassMessageUtil extends MessageUtil<MyClass> {}

调用方式:

MessageUtil<MyClass> messageUtil = new MyClassMessageUtil();

@Test
public void testMyClass() throws Exception {
    MyClass myClass = messageUtil.deserializeByStringConversion(IOUtils.toByteArray(request));
    Assert.assertNotNull(myClass);
}

方案3:传入TypeReference支持复杂泛型

如果需要反序列化List<MyClass>这类复杂泛型结构,可以让调用者直接传入TypeReference实例:

public class MessageUtil {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    public <T> T deserializeByStringConversion(final byte[] message, TypeReference<T> typeReference) throws Exception {
        String json = IOUtils.toString(message);
        return OBJECT_MAPPER.readValue(json, typeReference);
    }
}

调用方式:

MessageUtil messageUtil = new MessageUtil();

@Test
public void testMyClass() throws Exception {
    MyClass myClass = messageUtil.deserializeByStringConversion(IOUtils.toByteArray(request), new TypeReference<MyClass>() {});
    Assert.assertNotNull(myClass);
}

// 复杂泛型示例
@Test
public void testMyClassList() throws Exception {
    List<MyClass> myClassList = messageUtil.deserializeByStringConversion(IOUtils.toByteArray(listRequest), new TypeReference<List<MyClass>>() {});
    Assert.assertNotNull(myClassList);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:57:40