Spring Boot - Returning JSON with array of objects

Viewed 7823

Using this answer as a hint, I developed a Spring Boot controller for /greetings to return greeting in different languages in JSON.

While I am getting the output in the format (array of objects) I wanted, can you please let me know if there is a better way?

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;

@RestController
public class GreetingController {
    @GetMapping("/greetings")
    public HashMap<String, Object> getGreeting() {
        ArrayList<Object> al = new ArrayList<Object>();
        HashMap<String, String> map1 = new HashMap<String, String>();
        map1.put("en", "Greetings!");
        al.add(map1);
        HashMap<String, String> map2 = new HashMap<>();
        map2.put("hi", "Namaste!");
        al.add(map2);
        //
        HashMap<String, Object> finalMap = new HashMap<>();
        finalMap.put("all", al);
        return finalMap;
    }
}

Received (valid) output:

{
    "all": [
        {
            "en": "Greetings!"
        },
        {
            "hi": "Namaste!"
        }
    ]
}
2 Answers

If you just need array of objects you shouldn't choose map rather use List type. For example, if you have Greeting objects and you want to return a list of all greetings your GetMapping would be something like below:

@GetMapping("/greetings")
    public ResponseEntity<List<Map<String,String>>> getGreeting() {
        ArrayList<Object> al = new ArrayList<Object>();
        HashMap<String, String> map1 = new HashMap<String, String>();
        map1.put("en", "Greetings!");
        al.add(map1);
        HashMap<String, String> map2 = new HashMap<>();
        map2.put("hi", "Namaste!");
        al.add(map2);
        return ResponseEntity.ok(al);
    }

Using ResponseEntity ensures that you are returning right status code and as it's wrapping up your response, it's more maintainable.

Edit: For key value pairs that you have created using map you can use POJO.

class Greeting{
   private String greetingLangCode;
   private String greetingText;
   // getters and setters

 @JsonValue
public String info(){
 return this.greetingLangCode + ":" + "greetingText";
}
}

With this endpoint changes to

    @GetMapping("/greetings")
    public ResponseEntity<List<Greeting>> getGreeting() {
        List<Greeting> al = new ArrayList<>();
        al.add(new Greeting("en", "Greetings!"));
        al.add(new Greeting("hi", "Namaste!"));
        return ResponseEntity.ok(al);
    }

But in case above don't work, you will require a custom serializer to serialize like map's key:value pair.

Probably this post is helpful. This post have a complete example of how to write custom serializer and deserializer.

While you can use nested List and Map objects, your code becomes unreadable quite soon. It is better to create actual objects. E.g.:

public class TranslatedGreeting {
  private String language;
  private String greeting;

  public TranslatedGreeting(String language, String greeting) {
    this.language = language;
    this.greeting = greeting;
  }

  // add getters
}

Then use that in the controller:

@RestController
public class GreetingController {

  @GetMapping("/greetings")
  public List<TranslatedGreeting> greetings() {
    List<TranslatedGreeting> result = new ArrayList<>();
    result.add(new TranslatedGreeting("en", "Greetings!"));
    result.add(new TranslatedGreeting("hi", "Namaste!");
    return result;
  }
}

The resulting JSON will be like:

[
  {"language":"en","greeting":"Greetings!"},
  {"language":"hi","greeting":"Namaste!"},
]

This JSON structure is also easier to parse for clients as the keys are fixed on language and greeting.

If you really want to keep the original structure, declare a custom serializer like this:

@JsonComponent
public class TranslatedGreetingSerializer extends JsonSerializer<TranslatedGreeting> {
    @Override
    public void serialize(TranslatedGreeting translatedGreeting, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField(translatedGreeting.getLanguage(), translatedGreeting.getGreeting());
        jsonGenerator.writeEndObject();
    }
}

This will output:

[
  {
    "en": "Greetings!"
  },
  {
    "hi": "Namaste!"
  }
]
Related