0

I'm receiving JSON from REST API looks like:

{
    "items": [
        {
            "id": 60659,
            "name": "Display",
            "active": true,
            "account_id": 235
        },
        {
            "id": 36397,
            "name": " Mail Display",
            "active": true,
            "account_id": 107
        }
    ]
}

I'm using this method to parse it:

Mono<List<Item>> getItems(String token) {
        return webCLient
                .get()
                .headers(httpHeaders -> httpHeaders.setBearerAuth(token))
                .retrieve()
                .bodyToMono(ItemResponse.class)
                .map(ItemResponse::getResponse)
                .retryBackoff(RetrySettings.RETRIES, RetrySettings.FIRST_BACKOFF, RetrySettings.MAX_BACKOFF)
                .doOnError(e -> log.error("error: " + e.getCause().toString()))

Response:

public class ItemResponse {

    @JsonProperty("items")
    private List<Item> response;
}

But sometimes 3rd party API returns different response without top level items property and looks like:

[
        {
            "id": 60659,
            "name": "Display",
            "active": true,
            "account_id": 235
        },
        {
            "id": 36397,
            "name": " Mail Display",
            "active": true,
            "account_id": 107
        }
]

At this point my app is crashing with JSON decoding error. I used for this case:

bodyToMono(new ParameterizedTypeReference<List<Item>>() {})

But I can't always refactoring this part of code just to handle their json. How to do it in dynamical way with Spring WebFlux? Like try -> parse#1 -> catch -> parse#2. So i need to parse json in way#1 and if error occurs app should try to parse it with way#2.

3
  • 3
    the second response is not valid json, and should be fixed from the source since it is providing broken responses, but if the parsing fails, you could return a Mono#error and then use the onErrorResume and parse it in another way. Commented Apr 6, 2021 at 11:09
  • I fixed json, that was my bad. Can you, please, provide example? I work with Spring WebFlux few hours. Commented Apr 6, 2021 at 12:19
  • no i can't provide a working example, if you have only worked with it for a couple of hours, i suggest you read up more on how to work with it before you ask a question on stack overflow, and at least try and then post what you have tried and how it fails. Its part of the learning process as a developer. Commented Apr 6, 2021 at 12:21

1 Answer 1

1

You can get the response as a string .bodyToMono(String.class) and do whatever you want, with multiple try catches... but I think your best bet is to create a custom Deserializer and use it with your WebClient via ExchangeStrategies like described here: How to customize SpringWebFlux WebClient JSON deserialization? .

class MyResponse {

    List<Object> data;

    MyResponse(List<Object> data) {
        this.data = data;
    }
}

class MyResponseDeserializer extends JsonDeserializer<MyResponse> {

    @Override
    public MyResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);

        List<Object> data = new ArrayList<>();

        if (treeNode.isArray()) {
            // parse it as array
        } else {
            // parse it as object and put inside list
        }

        MyResponse myResponse = new MyResponse(data);

        return myResponse;
    }
}

And then

WebClient getWebClient() {
    ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addDeserializer(MyResponse.class, new MyResponseDeserializer());

    objectMapper.registerModule(simpleModule);

    ExchangeStrategies strategies = ExchangeStrategies
            .builder()
            .codecs(clientDefaultCodecsConfigurer -> {
                clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
                clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
            }).build();

    return WebClient.builder().exchangeStrategies(strategies).build();
}

Mono<List<Item>> getItems(String token) {
        return getWebClient()
                .get()
                .headers(httpHeaders -> httpHeaders.setBearerAuth(token))
                .retrieve()
                .bodyToMono(MyResponse.class)
                .map(MyResponse::data)
                .retryBackoff(RetrySettings.RETRIES, RetrySettings.FIRST_BACKOFF, RetrySettings.MAX_BACKOFF)
                .doOnError(e -> log.error("error: " + e.getCause().toString()))
}

The rest is the same as in your example just change the class name and add appropriate fields. And of course this is just a fast written demo and everything hardcoded and within a one method, better to have them injected

Sign up to request clarification or add additional context in comments.

1 Comment

Can you please guide me here: stackoverflow.com/questions/70907506/…?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.