How to parse a Spring 5 WebClient response in a non-blocking way?

12,060

You need to create a type that corresponds to the response sent by the server. A very minimal example could be like this:

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherResponse {
    public MainWeatherData main;
}

and the MainWeatherData class could be:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MainWeatherData {
    public String temp;
}

Finally, you could use WeatherResponse in bodyToMono:

...
   .retrieve()
   .bodyToMono(WeatherResponse.class);

The @JsonIgnoreProperties(ignoreUnknown = true)annotation instructs Jackson to not give any errors if it encounters any value in JSON string that is not present in you POJO.

You can access the WeatherResponseobject with a chained map operator:

getWeatherByCityName(cityName)
     .map(weatherResponse -> weatherResponse.main.temp)  
Share:
12,060
Michel Nagme
Author by

Michel Nagme

Updated on July 25, 2022

Comments

  • Michel Nagme
    Michel Nagme over 1 year

    I'm using Spring WebFlux WebClient to retrieve data from an external API, like this:

    public WeatherWebClient() {
        this.weatherWebClient = WebClient.create("http://api.openweathermap.org/data/2.5/weather");
    }
    
    public Mono<String> getWeatherByCityName(String cityName) {
        return weatherWebClient
                .get()
                .uri(uriBuilder -> uriBuilder
                                    .queryParam("q", cityName)
                                    .queryParam("units", "metric")
                                    .queryParam("appid", API_KEY)
                                    .build())
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(String.class);
    }
    

    This works fine and produces a response like this:

    {
        "coord":{
            "lon":-47.06,
            "lat":-22.91
        },
        "weather":[
        {
            "id":800,
            "main":"Clear",
            "description":"clear sky",
            "icon":"01d"
        }
        ],
        "base":"stations",
        "main":{
            "temp":16,
            "pressure":1020,
            "humidity":67,
            "temp_min":16,
            "temp_max":16
        },
        "visibility":10000,
        "wind":{
            "speed":1,
            "deg":90
        },
        "clouds":{
            "all":0
        },
        "dt":1527937200,
        "sys":{
            "type":1,
            "id":4521,
            "message":0.0038,
            "country":"BR",
            "sunrise":1527932532,
            "sunset":1527971422
        },
        "id":3467865,
        "name":"Campinas",
        "cod":200
    }
    

    But I'm only interested in the "temp" property (main -> temp). How could I transform the response (using Jackson's ObjectMapper, for example) to return only "temp" value in a reactive/non-blocking way?

    I understand the first thing is replacing ".retrieve()" by ".exchange()" but I can't figure out how to make it work.

    PS: This is my first question here. Please let me know if I'm doing something wrong or if you need more details.

    Thanks!

  • Michel Nagme
    Michel Nagme over 5 years
    Thanks @MuratOzkan, it worked like a charm! I was also relived to see that Jackson is Spring WebFlux library of choice to do that and they have non-blocking JSON parsing since version 2.9.0: github.com/FasterXML/jackson-core/issues/57