4

I need to make a request to an HTTP endpoint having a query parameter represented as JSON using Spring RestTemplate.

restTemplate.getForObject(
    apiRoot + "/path" + "?object={myObject}",
    Response.class,
    new MyObject())

Here I need MyObject to be converted to JSON (and URL-encoded obviously). But RestTemplate just converts it to String with toString call instead. MyObject is convertable to JSON by Jackson. UriComponentsBuilder behaves the same way:

UriComponentsBuilder.fromHttpUrl(apiRoot)
    .path("/path")
    .queryParam("object", new MyObject()))
    .queryParam("access_token", accessToken)
    .toUri()

Is there a way to avoid calling ObjectMapper.writeValueAsString by hands?

Update: to clarify, in the result I need to have ?object={"key":42} in my URI (or in URL-encodeded form ?object=%7B%22key%22%3A42%7D) given MyObject has one property key with value equal to 42.

raindev
  • 1,017
  • 1
  • 12
  • 25
  • 2
    You could use `ObjectMapper.writeValueAsString` in `MyObject.toString()` if that's appropriate. – TheKojuEffect Apr 05 '16 at 10:20
  • I can do it but probably would not for two reasons. First to keep my DTOs independent of the Spring Context as I'll probably wire pre-configured `ObjectMapper` via context in a real project. And second, pretty vague one, I would rather not to introduce alternative path for conversion of objects to JSON and stay explicit by calling `ObjectMapper` directly when I need to. – raindev Apr 05 '16 at 10:52

1 Answers1

4

What is wrong with using writeValueAsString ? Can You explain?

The only solution that comes to my mind looks like (I don't think if there is a way for Jackson to know that this object should be serialized in that moment):

@Autowired
ObjectMapper objectMapper;

@Override
public void run(String... strings) throws Exception {

    String urlBase = "http://localhost:8080/path";

    RestTemplate restTemplate = new RestTemplate();

    String url;
    MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
    params.set("object", objectMapper.writeValueAsString(new MyObject()));

    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(urlBase).queryParams(params);
    url = builder.build().toUri().toString();

    LOGGER.info("Composed before decode: " + url);

    //restTemplate.getForObject(url, Void.class);

    url = URLDecoder.decode(url, "UTF-8");

    LOGGER.info("Composed after decode: " + url);
}

Output:

2016-04-05 16:06:46.811  INFO 6728 --- [main] com.patrykwoj.StackOverfloApplication    : Composed before decode: http://localhost:8080/path?object=%7B%22key%22:43%7D
2016-04-05 16:06:46.941  INFO 6728 --- [main] com.patrykwoj.StackOverfloApplication    : Composed after decode: http://localhost:8080/path?object={"key":43}

Edit:

I forgot to mention, that sending JSON object as request parameter is generally not a good idea. For example, You will probably face problem with curly brackets inside JSON.

patrykos91
  • 3,506
  • 2
  • 24
  • 30
  • To answer you edit first: yes, I agree that use of JSON for query parameters is not a particularly elegant idea. Sadly, that's what Facebook uses for some of its endpoints. – raindev Apr 06 '16 at 14:41
  • There's nothing particularly _bad_ with `writeValueAsString`. Actually that's what I ended up doing. The reason I wanted to get rid of such a calls is the same why we aren't generally compose request body as `String` manually but use objects automatically converted to JSON - convenience. To not have to deal with `JsonProcessingException` would make things simpler as well. – raindev Apr 06 '16 at 14:45
  • I don't think that the limitation is inherent to the problem. The same logic Jackson use to "know" what to serialize to JSON when writing request body could be applied to query parameters. The most obvious reason to not have it implemented is discouraging such a style of API design (given that it's under our control), obviously besides ever present historical reasons. – raindev Apr 06 '16 at 14:48