-
Notifications
You must be signed in to change notification settings - Fork 13
Parameterized Types
This page documents how retrofit-graphql handles Java ParameterizedType values passed from Retrofit's converter infrastructure, across both serialization layers: the legacy GraphQLJson seam (:compat) and the new GraphQLTransportCodec SPI (:serialization-api).
When Retrofit calls a converter, it passes the full Java reflection Type of the method's return/parameter type. For generic types, this is a java.lang.reflect.ParameterizedType:
suspend fun getCurrentUser(
@Body request: GraphQLOperationRequest<EmptyGraphQLVariables>,
): Response<GraphQLResponse<GetCurrentUserData>>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ParameterizedType: GraphQLResponse<GetCurrentUserData>The converter must resolve this to a concrete serializer capable of deserializing GraphQLResponse<GetCurrentUserData>, not just GraphQLResponse<*>.
The backend-neutral GraphQLRequestConverter/GraphQLResponseConverter forward the full parameterized Type to the codec:
-
Retrofit passes the
ParameterizedTypetoGraphQLResponseConverter -
GraphQLResponseConverter forwards it to
GraphQLTransportCodec.decodeResponse(body, responseType) - The codec resolves a serializer/adapter for the full parameterized type
-
KotlinxGraphQLTransportCodec uses
kotlinx.serialization.serializer(type: java.lang.reflect.Type)-- a JVM reflection extension fromkotlinx-serialization-jsonthat handlesClass<*>,ParameterizedType,GenericArrayType, andWildcardType, recursively resolving serializers for all type arguments. -
GsonGraphQLTransportCodec uses
Gson.getAdapter(TypeToken.get(type))(viafromJson), which natively supportsParameterizedType.
The same flow applies on the request side: GraphQLRequestConverter forwards the body parameter's Type (e.g. GraphQLOperationRequest<GetMarketPlaceAppsVariables>) to GraphQLTransportCodec.encodeRequest(request, requestType) so the codec can resolve the typed variables serializer from the declared generic argument rather than the runtime class.
The deprecated GraphResponseConverter/GraphRequestConverter forward the same ParameterizedType to GraphQLJson.decode(json, type) / encode(value, type):
-
KotlinxGraphQLJson uses
kotlinx.serialization.serializer(type)(JVM reflection extension). ForGraphContainer<GetCurrentUserData>it resolves theGraphContainerserializer, theGetCurrentUserDataserializer, and recursively any nested type parameters. -
GsonGraphQLJson uses
Gson.getAdapter(TypeToken.get(type)), which natively supportsParameterizedType.
Prior to v0.13.x, KotlinxGraphQLJson attempted to resolve the serializer using only the raw class (losing type arguments):
// Broken: loses GetCurrentUserData type argument
val serializer = serializer<GraphContainer<*>>()This failed at runtime with a SerializationException because kotlinx could not find a serializer for the erased type GraphContainer<*>.
Using serializer(type: java.lang.reflect.Type), the full ParameterizedType is passed through, and kotlinx can resolve the correct KSerializer<GraphContainer<GetCurrentUserData>>.
Your decodeResponse(body, responseType) must resolve adapters for ParameterizedType:
class CustomGraphQLTransportCodec : GraphQLTransportCodec {
override fun <T : Any> decodeResponse(body: String, responseType: Type): T {
// responseType may be Class<T> or ParameterizedType (e.g., GraphQLResponse<Foo>)
// Your JSON library must support resolving adapters for parameterized types.
// Failures must be wrapped in GraphQLResponseDecodingException.
}
override fun encodeRequest(request: GraphQLOperationRequest<*>, requestType: Type): String {
// requestType may be ParameterizedType (e.g., GraphQLOperationRequest<FooVariables>)
// Failures must be wrapped in GraphQLRequestEncodingException.
}
}Common patterns:
-
Moshi:
moshi.adapter<T>(type).fromJson(json) -
Jackson:
objectMapper.readValue(json, TypeFactory.constructType(type)) -
Gson:
gson.fromJson(json, type)(passesTypedirectly) -
Kotlinx:
serializer(type: Type)(JVM reflection extension)
The same requirement applies to decode(json, type) on the legacy seam: type may be Class<T> or ParameterizedType (e.g., GraphContainer<Foo>).
- Serialization Backends -- the two serialization layers and custom backend contracts