Возврат объекта JSON в качестве ответа в Spring Загрузка

У меня есть образец RestController в Spring Boot:

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

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


}

Я использую библиотеку JSON org.json

Когда я нажимаю API /hello, я получаю исключение, говорящее:

Servlet.service() для сервлета [dispatcherServlet] в контексте с путем [] вызвала исключение [Ошибка обработки запроса; Вложенное исключение - java.lang.IllegalArgumentException: не найдено преобразователя для возвращаемого значения типа: class org.json.JSONObject] с первопричиной.

java.lang.IllegalArgumentException: не найдено преобразователя для возвращаемого значения типа: класс org.json.JSONObject

В чем проблема? Может кто-нибудь объяснить, что именно происходит?

Ответ 1

Поскольку вы используете Spring Boot web, зависимость Джексона является неявной, и нам не нужно определять ее явно. Вы можете проверить зависимость Джексона в вашем pom.xml на вкладке иерархии зависимостей, если используете eclipse.

И поскольку вы аннотировали @RestController не нужно делать явное преобразование json. Просто верните POJO, и сериализатор Джексона позаботится о преобразовании в json. Это эквивалентно использованию @ResponseBody при использовании с @Controller. Вместо того, чтобы помещать @ResponseBody на каждый метод контроллера, мы @RestController вместо vanilla @Controller и @ResponseBody по умолчанию применяется ко всем ресурсам в этом контроллере.
Перейдите по этой ссылке: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

Проблема, с которой вы сталкиваетесь, заключается в том, что возвращаемый объект (JSONObject) не имеет метода получения для определенных свойств. И вы намерены не сериализовать этот JSONObject, а вместо этого сериализовать POJO. Так что просто верните POJO.
Перейдите по этой ссылке: fooobar.com/questions/144648/...

Если вы хотите вернуть сериализованную строку json, просто верните строку. В этом случае Spring будет использовать StringHttpMessageConverter вместо JSON-конвертера.

Ответ 2

Причина, по которой ваш текущий подход не работает, заключается в том, что Джексон по умолчанию используется для сериализации и десериализации объектов. Однако он не знает, как сериализовать JSONObject. Если вы хотите создать динамическую структуру JSON, вы можете использовать Map, например:

@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;
}

Это приведет к следующему ответу JSON:

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

Это немного ограничено, так как может быть немного сложнее добавить дочерние объекты. У Джексона есть свой механизм, используя ObjectNode и ArrayNode. Чтобы использовать его, вы должны autwire ObjectMapper в вашем сервисе/контроллере. Затем вы можете использовать:

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

Этот подход позволяет вам добавлять дочерние объекты, массивы и использовать все различные типы.

Ответ 3

Вы можете либо вернуть ответ в виде String как предложено @vagaasen, либо использовать объект ResponseEntity предоставленный Spring, как показано ниже. Таким образом, вы также можете вернуть Http status code который более полезен при вызове веб-сервиса.

@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);
    }
}

Ответ 4

вы также можете использовать hashmap для этого

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

Ответ 5

@RequestMapping("/api/status")
public Map doSomething()
{
    return Collections.singletonMap("status", myService.doSomething());
}

PS. Работает только для 1 значения