Returning JSON object as response in Spring Boot

449,092

Solution 1

As you are using Spring Boot web, Jackson dependency is implicit and we do not have to define explicitly. You can check for Jackson dependency in your pom.xml in the dependency hierarchy tab if using eclipse.

And as you have annotated with @RestController there is no need to do explicit json conversion. Just return a POJO and jackson serializer will take care of converting to json. It is equivalent to using @ResponseBody when used with @Controller. Rather than placing @ResponseBody on every controller method we place @RestController instead of vanilla @Controller and @ResponseBody by default is applied on all resources in that controller.
Refer this link: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

The problem you are facing is because the returned object(JSONObject) does not have getter for certain properties. And your intention is not to serialize this JSONObject but instead to serialize a POJO. So just return the POJO.
Refer this link: https://stackoverflow.com/a/35822500/5039001

If you want to return a json serialized string then just return the string. Spring will use StringHttpMessageConverter instead of JSON converter in this case.

Solution 2

The reason why your current approach doesn't work is because Jackson is used by default to serialize and to deserialize objects. However, it doesn't know how to serialize the JSONObject. If you want to create a dynamic JSON structure, you can use a Map, for example:

@GetMapping
public Map<String, String> sayHello() {
    HashMap<String, String> map = new HashMap<>();
    map.put("key", "value");
    map.put("foo", "bar");
    map.put("aa", "bb");
    return map;
}

This will lead to the following JSON response:

{ "key": "value", "foo": "bar", "aa": "bb" }

This is a bit limited, since it may become a bit more difficult to add child objects. Jackson has its own mechanism though, using ObjectNode and ArrayNode. To use it, you have to autowire ObjectMapper in your service/controller. Then you can use:

@GetMapping
public ObjectNode sayHello() {
    ObjectNode objectNode = mapper.createObjectNode();
    objectNode.put("key", "value");
    objectNode.put("foo", "bar");
    objectNode.put("number", 42);
    return objectNode;
}

This approach allows you to add child objects, arrays, and use all various types.

Solution 3

You can either return a response as String as suggested by @vagaasen or you can use ResponseEntity Object provided by Spring as below. By this way you can also return Http status code which is more helpful in webservice call.

@RestController
@RequestMapping("/api")
public class MyRestController
{

    @GetMapping(path = "/hello", produces=MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> sayHello()
    {
         //Get data from service layer into entityList.

        List<JSONObject> entities = new ArrayList<JSONObject>();
        for (Entity n : entityList) {
            JSONObject entity = new JSONObject();
            entity.put("aa", "bb");
            entities.add(entity);
        }
        return new ResponseEntity<Object>(entities, HttpStatus.OK);
    }
}

Solution 4

you can also use a hashmap for this

@GetMapping
public Map<String, Object> get() {
    Map<String, Object> map = new HashMap<>();
    map.put("key1", "value1");
    map.put("results", somePOJO);
    return map;
}

Solution 5

More correct create DTO for API queries, for example entityDTO:

  1. Default response OK with list of entities:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
    return entityService.getAllEntities();
}

But if you need return different Map parameters you can use next two examples
2. For return one parameter like map:

@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
    return ResponseEntity.status(HttpStatus.CREATED).body(
            Collections.singletonMap("key", "value"));
}
  1. And if you need return map of some parameters(since Java 9):
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getSomeParameters() {
    return ResponseEntity.status(HttpStatus.OK).body(Map.of(
            "key-1", "value-1",
            "key-2", "value-2",
            "key-3", "value-3"));
}
Share:
449,092
iwekesi
Author by

iwekesi

Updated on February 20, 2022

Comments

  • iwekesi
    iwekesi over 2 years

    I have a sample RestController in Spring Boot:

    @RestController
    @RequestMapping("/api")
    class MyRestController
    {
        @GetMapping(path = "/hello")
        public JSONObject sayHello()
        {
            return new JSONObject("{'aa':'bb'}");
        }
    }
    

    I am using the JSON library org.json

    When I hit API /hello, I get an exception saying :

    Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type: class org.json.JSONObject] with root cause

    java.lang.IllegalArgumentException: No converter found for return value of type: class org.json.JSONObject

    What is the issue? Can someone explain what exactly is happening?

  • iwekesi
    iwekesi about 7 years
    what is mapper here?
  • g00glen00b
    g00glen00b about 7 years
    @iwekesi that's the Jackson ObjectMapper you should autowire (see the paragraph above my last code snippet).
  • iwekesi
    iwekesi about 7 years
    If I add JSONObject in entities, it is again giving me similar exception
  • prem kumar
    prem kumar about 7 years
    if json string is what you want to return from java then you can just return a string if it is already json serialized. No need to convert string to json and json back to string.
  • Vihung
    Vihung almost 7 years
    If you want to return a set of name-value pairs without a rigid compile-time structure, you could return a Map<String,Object> or a Properties object
  • divine
    divine over 6 years
    @Sangam your answer helped me for another problem related to jackson-dataformat-xml
  • jones-chris
    jones-chris over 6 years
    This was a big help! Thank you!
  • Orkun Ozen
    Orkun Ozen over 6 years
    @prem kumar random question : what do you mean with 'instead of vanilla Controller and ResponseBody'? what s vanilla here?
  • prem kumar
    prem kumar over 6 years
    i meant a usual Controller and with ResponseBody annotation placed on every request method.
  • Melvin Roest
    Melvin Roest almost 6 years
    I wonder why this answer isn't upvoted more. I'm new to Spring too, so I don't know if this is a good software engineering practice. With that said, this answer really helped me. However, I did have a lot of trouble with a JSONObject, but since Spring uses Jackson I changed it to a HashMap instead and then the code that I read in this answer did work.
  • Sandeep Mandori
    Sandeep Mandori almost 6 years
    +1 for suggesting the MediaType.APPLICATION_JSON_VALUE as it ensures that produced result get parsed as json not xml as may happen if you don't define
  • xmlParser
    xmlParser over 5 years
    Where did you declare your entityList?
  • Sangam Belose
    Sangam Belose over 5 years
    @xmlParser its assumed to be received from service layer. Here we dont do any IDE validation whether you have declared the list and all those things. Its considered by default.Please read the question carefully. Understand the question carefully before downvoting.
  • xmlParser
    xmlParser over 5 years
    @SangamBelose nvm I have done it manually... Changed the vote too.
  • cogitoergosum
    cogitoergosum over 4 years
    It is stunning to know that one has to go such lengths for producing meaningful JSON objects! It is also sad that Pivotal makes no effort at all (spring.io/guides/gs/actuator-service) to call out these limitations. Luckily, we have SO! ;)
  • sak
    sak over 3 years
    it is returning this for me: [ { "empty": false } ]
  • Sangam Belose
    Sangam Belose over 3 years
    @sak you need to read the data into entityList from some source.
  • sak
    sak over 3 years
    what source? can you please name any?
  • Sangam Belose
    Sangam Belose over 3 years
    @sak The source can be anything, may be service layer getting data from database or any file storage.
  • Mahmoud Magdy
    Mahmoud Magdy over 2 years
    where mapper come from sorry I'm beginner and i tried this code nothing name mapper
  • g00glen00b
    g00glen00b over 2 years
    @MahmoudMagdy It's an ObjectMapper. If you use Spring boot with Spring web, you can autowire it in any other Spring bean.
  • Cletus Ajibade
    Cletus Ajibade over 2 years
    In addition to what @premkumar said, you can check out this reference link docs.spring.io/spring-boot/docs/current/reference/htmlsingle‌​/…, Spring MVC uses the HttpMessageConverter interface to convert HTTP requests and responses using the Jackson library by default.