I am trying to implement a GitHub REST API client in Spring. I'm new to Spring but reasonably familiar with JSON and Flux.
The problem I have is that the JSON body returned by the GitHub API can sometimes be an array and sometimes a single object, i.e.
{
foo="bar"
}
or
[
{
foo="bar"
},
{
foo="foobar"
}
]
I'm using ClientResponse#bodyToMono
with a ParameterizedTypeReference
like so:
WebClient client;
ParameterizedTypeReference<List<T>> typeRef;
...
client.get().uri(...).exchangeToMono(response -> {
return response.bodyToMono(typeRef);
});
The above works fine when the JSON response is an array, but (understandably) fails when it's not:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type `java.util.ArrayList<...>` from Object value (token `JsonToken.START_OBJECT`)
I've tried the following approaches to resolve, but none have succeeded:
- Enabling the ACCEPT_SINGLE_VALUE_AS_ARRAY option. I think this isn't working because it's not a field that may or may not be a list, but the entire JSON body.
- Creating a wrapper class
MyList<T>
that contains a singleList<T>
field. This fails to deserialize as there is no inner field in the JSON to map - perhaps there's some way I can fix this with annotations? - Checking if the response body is a list or not, and deserialize in two different ways. This code fails because
response.bodyToMono
can't be executed twice for Flux-y reasons (it'snull
the second time), so the following doesn't work:
Class<T> theClazz;
...
response.bodyToMono(String.class).flatMap(b -> b.startWith("[") ? response.bodyToMono(typeRef) : response.bodyToMono(theClazz).map(List::of))
- Writing my own
BodyExtractor
. I couldn't get the JacksonObjectMapper
to play nicely with thetypeRef
. UsingResolvableType.forType(typeRef).getRawClass()
with the mapper picks up that there's aList
, but doesn't properly deserialize the contents (instead putting it into a Map)
Any suggestions of how I can resolve this?
I feel like there must be a simple solution that I'm missing due to ignorance - any help much appreciated!