从嵌套类型派生包装类型的 TypeReference

问题描述 投票:0回答:1

Jackson 的

TypeReference
允许通过子类化具体化类型的泛型类型参数。示例:

final var typeRef = new TypeReference<List<MyType<MyArg>>>() {};
final List<MyType<MyArg>> list = mapper.readValue(input, typeRef);
final MyType<MyArg> first = list.get(0);

这仅在子分类时已知完整类型的情况下才有效。在通用方法中提取转换调用将不起作用

<T> T convert(final String input) {
  final var typeRef = new TypeReference<List<MyType<T>>>() {};
  final List<MyType<T>> list = mapper.readValue(input, typeRef);
  return list;
}

final MyType<MyArg> first = list.get(0);

(因为它会擦除到

new TypeReference<List<MyType<Object>>>() {}
,这可能会被反序列化为
List<MyType<Map<String, Object>>>
)。


我想反序列化和解包泛型类型的实例,而不在调用站点提供完整的类型签名。调用站点应该只关心内部(包装)类型,因为这是它将与之交互的唯一类型。给出以下记录定义:

private record MyResponse<T>(MyResult<T> result) {}

private record MyResult<T>(List<T> values) {}

private record MyStringValue(String name) {
    @Override public String toString() {
        return "'" + name + "'";
    }
}

和方法

<T> MyResult<T> convert(final String input, final TypeReference<MyResponse<T>> typeRef) {
    try {
        return objectMapper.readValue(input, typeRef).result();
    } catch (final JsonProcessingException ex) {
        throw new RuntimeException(ex);
    }
}

如何解开此函数的结果并仅返回

T
的实例,而不提供完整类型引用
TypeReference<MyResponse<MyResult<T>>>

我有:

<T> List<T> unwrap(final String input, final TypeReference<MyResponse<T>> typeRef) {
    return convert(content, typeRef).values();
}

必须称为:

final List<MyStringValue> values = unwrap(input, new TypeReference<MyResponse<MyResult<MyStringValue>>>() {});
// or with diamond operator:
final List<AnotherType> values = unwrap(input, new TypeReference<>() {});

unwrap
的呼叫者不需要了解
MyResponse
MyResult
。是否可以通过隐藏这些实现细节的方式定义
unwrap

final List<MyStringValue> values = unwrap(input, new TypeReference<MyStringValue>() {});

<T> List<T> unwrap(final String input, final TypeReference<T> typeRef) { // <- callers do not need to know about MyResponse/MyResult
    final TypeReference<MyResponse<MyResult<T>>> wrappedTypeRef = typeRef.wrap(MyResponse<MyResult<T>>.class); // obviously not valid Java syntax
    return convert(content, typeRef).values();
}

或者由于类型擦除,我只是坚持向此方法的所有调用者公开完整类型?

MyResponse
<T>
在实际代码中都是不同的具体类型:
MyResponse
有一个父/子类,
T
有3个实现)

java json generics jackson objectmapper
1个回答
0
投票

TypeReference
仅设计为通过子类化它来创建。

您可以添加一个新的

convert
重载,它需要
JavaType
,您可以使用
TypeFactory
动态创建它。

<T> MyResult<T> convert(final String input, final JavaType typeRef) {
    try {
        return objectMapper.<MyResponse<T>>readValue(input, typeRef).result();
    } catch (final JsonProcessingException ex) {
        throw new RuntimeException(ex);
    }
}

<T> MyResult<T> convert(final String input, final TypeReference<MyResponse<T>> typeRef) {
    return convert(input, TypeFactory.defaultInstance().constructType(typeRef));
}

使用

constructParametricType
中的
unwrap
创建您想要的类型:

<T> List<T> unwrap(final String input, final TypeReference<T> typeRef) {
    var typeFactory = TypeFactory.defaultInstance();
    var responseResultType = typeFactory.constructParametricType(
            MyResponse.class,
            typeFactory.constructParametricType(
                    MyResult.class,
                    typeFactory.constructType(typeRef)
            )
    );
    return this.<T>convert(input, responseResultType).values();
}
© www.soinside.com 2019 - 2024. All rights reserved.