Home > Blockchain >  WebClient does not return a "valid" list of Strings
WebClient does not return a "valid" list of Strings

Time:04-20

I have a spring boot app that among others, has an endpoint that when hit, returns a list of strings. I also have another spring boot app that hits the first app's endpoint to get the data. The fetch code:

return webClient.get().uri("/sensors/get-cities").headers(httpHeaders -> {
      httpHeaders.set("Authorization", auth);
    }).retrieve()
        .bodyToFlux(String.class).collectList().block();

The above yields a list but with this format when I inspect it in the debbuger, "["city"]". The outer double quotes, I get them because it's a string but the brackets and the inside double quotes, I do not. I tried replacing these characters but I had no luck with the brackets (tried regex). It is like they are not there, but at the same time they are. I am confused at this point. But I think that the behavior of the fetch code is not normal, it should yield a valid array of strings.

CodePudding user response:

Your Springboot API returns result as parsed to JSON (this is default behavior). So it first builds a list of Strings (in your case just a single String "city" and than serializes it to Json. In this case since it is a list it serializes it to JSON array as opposed to JSON Object. Read about JSON here. So in your second Springboot app that hits the API from the first one should assume that you are getting JSON which you need to parse to get your list. To parse it you can use readValue() method of ObjectMapper class of Json Jackson library which is a default JSON library in Springboot. your code would be

List<String> myList;
ObjectMapper = new ObjectMapper();
//Add setters for ObjectMapper configuration here if you want a specific config
try {
  myList = objectMapper.readValue(myJsonString, List.class);
} catch(IOException ioe) {
...
}

In addition I wrote my own Open-source library called MgntUtils, that includes JsonUtils class which is a thin wrapper over Json Jackson library. It provides just Json parser and serializer, but in many cases that is all you need. With my library you would only need one dependency as oppose to Jackson, and JsonUtils class just have 4 methods, so by far easier to understand. But in your case if you use my library the code would be very similar to the above code. It would be something like this:

List<String> myList;
try {
  myList = JsonUtils.readObjectFromJsonString(myJsonString, List.class);
} catch(IOException ioe) {
...
}

Note that in this case you won't have to instantiate and configure ObjectMapper instance as readObjectFromJsonString is a static method. Anyway if you are interested in using my library you can find maven artifacts here and The library itself with source code and javadoc is on Github here. Javadoc for JsonUtils class is here

CodePudding user response:

What you are probably getting (im guessing here) is a response body that looks something like this:

[
    "New York",
    "Madrid",
    "London"
]

You then tell webflux that you want to convert the body to a Flux of String by calling bodyToFlux(String.class).

So the framework takes the entire response and makes a string out of it

// A string of the entire array (im escaping the quotation marks)
"[\"New York\",\"Madrid\",\"London\"]"

And then the framework will throw the entire thing into a Flux which means it takes the first position in the Flux. You then emit all the values into a List by calling collectList The equivalent code is sort of:

List<String> oneString = Flux.just("[\"New York\",\"Madrid\",\"London\"]")
    .collectList()
    .block();

So you get a list, with one string in it, which is the entire body.

What you probably want to do is to get a list out if it. And this is one way to do it:

List<String> strings =  webClient.get()
                          .uri("/sensors/get-cities")
                          .headers(httpHeaders -> {
                              httpHeaders.set("Authorization", auth);
                          })
                          .retrieve()
                          .bodyToMono(new ParameterizedTypeReference<List<String>>() {})
                          .block();

Spring explains ParameterizedTypeReference:

The purpose of this class is to enable capturing and passing a generic Type. In order to capture the generic type and retain it at runtime

So its sort of a class that makes sure we can use generic types like List<T> and helps us with type information.

So what we do is that we now take the response and tell the framework that it the body is a list of strings directly. We dont need to do collectList anymore as the framework will stick it in a list directly instead. We then call block to wait in the response.

  • Related