2

I'm trying to map certain values from request query parameters to POJO parameters as the following. This is the POJO:

import lombok.*;
import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Map;

@Data
@Builder
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    @NotNull(message = "personId cannot be null")
    @Min(value = 0)
    private Long personId;

    @NotNull(message = "from date cannot be null")
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private LocalDateTime from;

    @NotNull(message = "to date cannot be null")
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private LocalDateTime to;

    private Map<String, String> filters;
}

And this is the controller:

    @GetMapping(path = "/persons")
    public ResponseEntity<Integer> getPersons(@Valid final Person personRequest) throws Exception {
        return ResponseEntity.ok(personService.getPersonsCount(personRequest));
    }

I want to map request query parameters to the attributes of this pojo. This is the expected request:

{application_url}/persons?personId=12&from=2017-03-22T00:00:00&to=2019-03-22T00:00:00&country=UK&company=xyz

I want to map personId, from, to to corresponding attributes in POJO and map the rest of query parameters nationality, company to the filters map. In other words personId, to, from are only static in the request and the rest of the parameters may vary, we can have salary=1000&minAage=31 instead of country=UK&company=xyz so I want to map the rest of the parameters to the filters map.

Is there a way to achieve so?

m.y.m
  • 347
  • 4
  • 27
  • @Eklavya, it's a GET request and required to be as query parameters. – m.y.m Jun 04 '20 at 15:28
  • Use `@RequestParam Map requestParams` to get all request param then prepare `Person` from `requestParams` map manually – Eklavya Jun 04 '20 at 15:33
  • I see your point but I was wondering if there is more direct way to achieve this. – m.y.m Jun 04 '20 at 16:19
  • May be not possible and it's logical – Eklavya Jun 04 '20 at 16:26
  • I don't think it's possible unless you create and hook in your own `HandlerMethodArgumentResolver`. Take a look at `RequestParamMapMethodArgumentResolver` - which is how it converts query parameters into a map as Eklavya mentioned. – Tim Tong Jun 05 '20 at 00:14

1 Answers1

2

The way you can achieve it is to use @RequestParam Map<String, String> filters with other request parameters or just to use map for request parameters. Because all parameters would be included in map.


Example with all parameters in request:

@GetMapping(path = "persons")
public ResponseEntity<Integer> getPersons(@RequestParam Long personId,
                                          @RequestParam String from,
                                          @RequestParam String to,
                                          @RequestParam Map<String, String> filters) {
    Person person = Person.builder()
            .personId(personId)
            .from(getDate(from))
            .to(getDate(to))
            .filters(filterMap(filters))
            .build();
    return ResponseEntity.ok(personService.getPersonsCount(person));
}

private LocalDateTime getDate(String date) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    return LocalDateTime.parse(date, formatter);
}

private Map<String, String> filterMap(Map<String, String> request) {
    request.remove("personId");
    request.remove("from");
    request.remove("to");
    return request;
}

The code is totally messed up. And since Map<String, String> filters is @RequestParam, so it includes all request parameters even what's defined above. So you need to remove pesonId, from, to if you don't want to be in filters map.

Example with request map only

@GetMapping(path = "persons")
public ResponseEntity<Integer> getPerson(@RequestParam Map<String, String> requestParams) {
    Person person = Person.builder()
             .personId(Long.valueOf(requestParams.get("personId")))
             .from(getDate(requestParams.get("from")))
             .to(getDate(requestParams.get("to")))
             .filters(filterRequest(requestParams))
             .build();
    return ResponseEntity.ok(personService.getPersonsCount(person));
}

private LocalDateTime getDate(String date) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    return LocalDateTime.parse(date, formatter);
}

private Map<String, String> filterRequest(Map<String, String> request) {
    request.remove("personId");
    request.remove("from");
    request.remove("to");
    return request;
}  

However at least I don't know nice or simple configuration to convert path parameters to POJO before calling controller endpoint, so I suggest you to use something from these examples. Otherwise use request body or try to use this example

Dumbo
  • 1,630
  • 18
  • 33
  • I see it's the easiest solution for this case as mention also by @Eklavya above. – m.y.m Jun 05 '20 at 10:53
  • Good writing! @m.y.m It's not possible the way you think because you can't map `a map` into a single query param. Hope you understand. – Eklavya Jun 05 '20 at 15:32