Jackson's TypeReference allows reifying the generic type parameters of a type via subclassing. Example:
final var typeRef = new TypeReference<List<MyType<MyArg>>>() {}; final List<MyType<MyArg>> list = mapper.readValue(input, typeRef); final MyType<MyArg> first = list.get(0); This only works if the full type is known at time of sub classing. Extracting the conversion call in a generic method will not work:
<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); (because it erases to new TypeReference<List<MyType<Object>>>() {}, which will likely be deserialized to List<MyType<Map<String, Object>>>).
I want to deserialize and unwrap instances of generic types, without providing the full type signature at the call site. The call site should only be concerned about the inner (wrapped) type, since that's the only type it will interact with. Given the following record definitions:
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 + "'"; } } and a method
<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); } } how can I unwrap the result of this function and only return an instance of T, without providing the full type ref TypeReference<MyResponse<MyResult<T>>>?
I have:
<T> List<T> unwrap(final String input, final TypeReference<MyResponse<T>> typeRef) { return convert(content, typeRef).values(); } which must be called as:
final List<MyStringValue> values = unwrap(input, new TypeReference<MyResponse<MyResult<MyStringValue>>>() {}); // or with diamond operator: final List<AnotherType> values = unwrap(input, new TypeReference<>() {}); Callers of unwrap should not need to know about MyResponse nor MyResult. Is it possible to define unwrap in such a way that these implementation details are hidden?
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(); } or am I simply stuck with exposing the full type to all callers of this method due to type erasure?
(Both MyResponse and <T> will be different concrete types in the real code: MyResponse has a parent/child class and T has 3 implementations)
TypeReferenceisJavaType. Can you changeconvertto take aJavaTypeas parameter instead?T, wouldn't it be possible to find a common upper bound and define a custom deserializer like shown in this answer stackoverflow.com/questions/78684321/…?TypeFactory.constructParametricTypeto wrap some existing type into another generic type. It really just depends on whether you can changeconvert's parameter type, or add another overload ofconvert.