简体   繁体   English

在 Spring Boot 中返回 JSON 对象作为响应

[英]Returning JSON object as response in Spring Boot

I have a sample RestController in Spring Boot:我在 Spring Boot 中有一个示例 RestController:

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

I am using the JSON library org.json我正在使用 JSON 库org.json

When I hit API /hello , I get an exception saying :当我点击 API /hello时,我得到一个异常说:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; Servlet.service() 用于路径 [] 上下文中的 servlet [dispatcherServlet] 引发异常 [请求处理失败; 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] 根本原因

java.lang.IllegalArgumentException: No converter found for return value of type: class org.json.JSONObject java.lang.IllegalArgumentException:没有找到类型的返回值的转换器:类 org.json.JSONObject

What is the issue?问题是什么? Can someone explain what exactly is happening?有人可以解释到底发生了什么吗?

As you are using Spring Boot web, Jackson dependency is implicit and we do not have to define explicitly.当您使用 Spring Boot web 时,Jackson 依赖是隐式的,我们不必明确定义。 You can check for Jackson dependency in your pom.xml in the dependency hierarchy tab if using eclipse.如果使用 eclipse,您可以在依赖层次结构选项卡中的pom.xml中检查 Jackson 依赖项。

And as you have annotated with @RestController there is no need to do explicit json conversion.并且由于您已使用@RestController进行注释,因此无需进行显式 json 转换。 Just return a POJO and jackson serializer will take care of converting to json.只需返回一个 POJO,jackson 序列化程序将负责转换为 json。 It is equivalent to using @ResponseBody when used with @Controller.与@Controller 一起使用时,相当于使用@ResponseBody 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.不是将@ResponseBody放置在每个控制器方法上,而是放置@RestController而不是vanilla @Controller ,默认情况下@ResponseBody应用于该控制器中的所有资源。
Refer this link: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody请参阅此链接: 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.您面临的问题是因为返回的对象(JSONObject)没有某些属性的 getter。 And your intention is not to serialize this JSONObject but instead to serialize a POJO.您的意图不是序列化这个 JSONObject,而是序列化一个 POJO。 So just return the POJO.所以只需返回POJO。
Refer this link: https://stackoverflow.com/a/35822500/5039001请参阅此链接: https : //stackoverflow.com/a/35822500/5039001

If you want to return a json serialized string then just return the string.如果你想返回一个 json 序列化的字符串,那么只需返回该字符串。 Spring will use StringHttpMessageConverter instead of JSON converter in this case.在这种情况下,Spring 将使用 StringHttpMessageConverter 而不是 JSON 转换器。

The reason why your current approach doesn't work is because Jackson is used by default to serialize and to deserialize objects.您当前的方法不起作用的原因是因为默认情况下使用 Jackson 来序列化和反序列化对象。 However, it doesn't know how to serialize the JSONObject .但是,它不知道如何序列化JSONObject If you want to create a dynamic JSON structure, you can use a Map , for example:如果要创建动态 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;
}

This will lead to the following JSON response:这将导致以下 JSON 响应:

{ "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 . Jackson 有自己的机制,使用ObjectNodeArrayNode To use it, you have to autowire ObjectMapper in your service/controller.要使用它,您必须在服务/控制器中自动装配ObjectMapper 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.这种方法允许您添加子对象、数组并使用所有各种类型。

You can either return a response as String as suggested by @vagaasen or you can use ResponseEntity Object provided by Spring as below.您可以按照@vagaasen 的建议将响应作为String返回,也可以使用 Spring 提供的ResponseEntity对象,如下所示。 By this way you can also return Http status code which is more helpful in webservice call.通过这种方式,您还可以返回Http status code ,这在 webservice 调用中更有帮助。

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

you can also use a hashmap for this您也可以为此使用哈希图

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

More correct create DTO for API queries, for example entityDTO:更正确的为 API 查询创建 DTO,例如 entityDTO:

  1. Default response OK with list of entities:实体列表的默认响应 OK:
 @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但是如果你需要返回不同的 Map 参数,你可以使用接下来的两个例子
2. For return one parameter like map: 2. 返回一个参数,如 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):如果您需要返回某些参数的映射(自 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")); }
@RequestMapping("/api/status")
public Map doSomething()
{
    return Collections.singletonMap("status", myService.doSomething());
}

PS.附注。 Works only for 1 value仅适用于 1 个值

use ResponseEntity<ResponseBean>使用ResponseEntity<ResponseBean>

Here you can use ResponseBean or Any java bean as you like to return your api response and it is the best practice.在这里,您可以根据需要使用 ResponseBean 或 Any java bean 来返回 api 响应,这是最佳实践。 I have used Enum for response.我使用 Enum 进行响应。 it will return status code and status message of API.它将返回 API 的状态代码和状态消息。

@GetMapping(path = "/login")
public ResponseEntity<ServiceStatus> restApiExample(HttpServletRequest request,
            HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        loginService.login(username, password, request);
        return new ResponseEntity<ServiceStatus>(ServiceStatus.LOGIN_SUCCESS,
                HttpStatus.ACCEPTED);
    }

for response ServiceStatus or(ResponseBody)用于响应 ServiceStatus 或(ResponseBody)

    public enum ServiceStatus {

    LOGIN_SUCCESS(0, "Login success"),

    private final int id;
    private final String message;

    //Enum constructor
    ServiceStatus(int id, String message) {
        this.id = id;
        this.message = message;
    }

    public int getId() {
        return id;
    }

    public String getMessage() {
        return message;
    }
}

Spring REST API should have below key in response Spring REST API 应该具有以下键作为响应

  1. Status Code状态码
  2. Message留言

you will get final response below您将在下面得到最终答复

{

   "StatusCode" : "0",

   "Message":"Login success"

}

you can use ResponseBody(java POJO, ENUM,etc..) as per your requirement.您可以根据您的要求使用 ResponseBody(java POJO、ENUM 等)。

If you need to return a JSON object using a String, then the following should work:如果您需要使用字符串返回 JSON 对象,则以下操作应该有效:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.ResponseEntity;
...

@RestController
@RequestMapping("/student")
public class StudentController {

    @GetMapping
    @RequestMapping("/")
    public ResponseEntity<JsonNode> get() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode json = mapper.readTree("{\"id\": \"132\", \"name\": \"Alice\"}");
        return ResponseEntity.ok(json);
    }
    ...
}

I use to return Map<String,Object> in the Controller by using the toMap() method of org.json.JSONObject as follows.我使用 org.json.JSONObject 的 toMap() 方法在控制器中返回 Map<String,Object> ,如下所示。

@GetMapping("/json")
public Map<String, Object> getJsonOutput() {       
    JSONObject jsonObject = new JSONObject();
    //construct jsonObject here
    return jsonObject.toMap();
}

you can do this :你可以这样做 :

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM