1

I want to return JSON from Rest API endpoint as keys with values. Example:

  {
    "terminal 1":
       {"date":"2018-10-06T00:00:00.000+0000","volume":111,"count":1},
    "terminal 2":
       {"date":"2018-11-06T00:00:00.000+0000","volume":122,"count":1}
  }

How I can add the keys? It should be I suppose like this:

List<String<List<TopTerminalsDTO>>>>

Can you give me some code example?

Latest attempt to clean the final code:

@GetMapping("/terminals")
public ResponseEntity<Map<Integer, List<TopTerminalsDTO>>> getTopTerminalsVolumes(
        @RequestParam(value = "start_date", required = true) String start_date,
        @RequestParam(value = "end_date", required = true) String end_date) {

        LocalDateTime start_datel = LocalDateTime.now(Clock.systemUTC());
        LocalDateTime end_datel = LocalDateTime.now(Clock.systemUTC());

        final List<PaymentTransactionsDailyFacts> list = dashboardRepository.top_daily_transactions(start_datel, end_datel);

        final Collector<PaymentTransactionsDailyFacts, List<TopTerminalsDTO>, List<TopTerminalsDTO>> terminalsCollector =
                 Collector.of(
                    ArrayList::new,
                    (terminals, p) -> terminals.add(mapper.toTopTerminalsDTO(p)),
                    (accumulator, terminals) -> {
                       accumulator.addAll(terminals);
                       return accumulator;
                    }
                 );

        final Map<Integer, List<TopTerminalsDTO>> final_map = 
                     list.stream()
                         .filter(p -> p.getTerminal_id() != null)
                         .collect(Collectors.groupingBy(p -> p.getTerminal_id(), terminalsCollector));

    return ResponseEntity.ok(final_map);
}

2 Answers 2

2

Following your JSON, testDate() should return Map<String, TopTerminalsDTO> instead of List.

Map<String, TopTerminalsDTO> result = newHashMap();

for (int i = 0; i <= 10; i++) {

    TopTerminalsDTO ttDto = new TopTerminalsDTO();
    ttDto.setCount(ThreadLocalRandom.current().nextInt(20, 500 + 1));

    LocalDate localDate = LocalDate.now().minus(Period.ofDays((new Random().nextInt(365 * 70))));
    Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());

    ttDto.setDate(date);
    ttDto.setVolume(ThreadLocalRandom.current().nextInt(300, 5000 + 1));

    result.put("terminal "+i, ttDto)
}

return result;

And, of course, change response type of rest method to ResponseEntity<Map<String, TopTerminalsDTO>>

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

Comments

1

This is what a Javascript dictionary looks like.
In Java, the correct representation is a Map<String, TopTerminalDto>.

Say you have an ordered List, and you want to return a Map with generated keys terminal{index}.

final List<TopTerminalDto> list = ...
final Map<String, TopTerminalDto> map = 
         IntStream.range(0, list.size())
                  .boxed()
                  .collect(toMap(i -> "terminal" + i, i -> list.get(i)));

The Spring endpoint would become:

@GetMapping("terminals")
Map<String, TopTerminalDto> getTopTerminalVolumes() { ... }

The ResponseEntity is not mandatory in Spring.

Remember to work as much as possible via Stream(s), to produce results without intermediate temporary state.

Additional example:

final List<PaymentTransactionsDailyFacts> list = 
            dashboardRepository.top_daily_transactions(start_datel, end_datel);

final Map<String, TopTerminalDto> map =
            list.stream()
                .collect(toMap(p -> p.getTerminal(), this::toDto))

// Conversion method
private TopTerminalDto toDto(final PaymentTransactionsDailyFacts p) {
    // Implement conversion to Dto
}

For multiple values associated with a terminal:

final Map<Integer, List<TopTerminalDto>> map = 
             list.stream()
                 .filter(p -> p.getTerminal() != null)
                 .collect(groupingBy(
                     p -> p.getTerminal(),
                     Collector.of(
                            ArrayList::new,
                            (terminals, p) -> terminals.add(toDto(p)),
                            (accumulator, terminals) -> {
                                accumulator.addAll(terminals);
                                return accumulator;
                            }
                     )
             ));

You can clean the code by extracting the Collector.

final Collector<Integer, List<TopTerminalDto>, List<TopTerminalDto>> terminalsCollector =
             Collector.of(
                ArrayList::new,
                (terminals, p) -> terminals.add(toDto(p)),
                (accumulator, terminals) -> {
                   accumulator.addAll(terminals);
                   return accumulator;
                }
             )

final Map<Integer, List<TopTerminalDto>> map = 
             list.stream()
                 .filter(p -> p.getTerminal() != null)
                 .collect(groupingBy(p -> p.getTerminal(), terminalsCollector));

18 Comments

Looks like to need to ask for help for this: Syntax error on token "::", ( expected
I don't have a constructor in toTopTerminalsDTO. It's a DTO.
@PeterPenzov just change :: to a single . (dot) if mapper is a local variable
But what about this line toTopTerminalsDTO(p) I don't have a constructor into the DTO: pastebin.com/TkHZPC7p
@PeterPenzov just change the method toTopTerminalDto() in a way to use setters instead of a constructor.
|

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.