在 Spring 引导中返回 JSON 对象作为响应
Returning JSON object as response in Spring Boot
我在 Spring 引导中有一个示例 RestController:
@RestController
@RequestMapping("/api")
class MyRestController
{
@GetMapping(path = "/hello")
public JSONObject sayHello()
{
return new JSONObject("{'aa':'bb'}");
}
}
我正在使用 JSON 库 org.json
当我点击 API /hello
时,出现异常:
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
这是什么问题?有人可以解释到底发生了什么吗?
您当前的方法不起作用的原因是默认情况下使用 Jackson 来序列化和反序列化对象。但是,它不知道如何序列化 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" }
这有点受限,因为添加子对象可能会变得有点困难。 Jackson 有自己的机制,使用 ObjectNode
和 ArrayNode
。要使用它,您必须在 service/controller 中自动装配 ObjectMapper
。然后你可以使用:
@GetMapping
public ObjectNode sayHello() {
ObjectNode objectNode = mapper.createObjectNode();
objectNode.put("key", "value");
objectNode.put("foo", "bar");
objectNode.put("number", 42);
return objectNode;
}
这种方法允许您添加子对象、数组,并使用各种类型。
您可以按照@vagaasen 的建议return 作为String
的响应,或者您可以使用Spring 提供的ResponseEntity
对象,如下所示。这样也可以return 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);
}
}
当您使用 Spring Boot web 时,Jackson 依赖是隐式的,我们不必明确定义。如果使用 eclipse,您可以在依赖层次结构选项卡中的 pom.xml
中检查 Jackson 依赖。
正如您用 @RestController
注释的那样,无需进行显式 json 转换。仅 return 一个 POJO 和 jackson 序列化程序将负责转换为 json。与@Controller 一起使用时相当于使用@ResponseBody
。我们没有在每个控制器方法上放置 @ResponseBody
,而是放置 @RestController
而不是 vanilla @Controller
,默认情况下 @ResponseBody
应用于该控制器中的所有资源。
参考这个 link: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
您遇到的问题是因为 returned 对象(JSON对象)的某些属性没有 getter。你的意图不是序列化这个 JSONObject 而是序列化一个 POJO。所以只是 return POJO。
参考这个 link:
如果你想 return 一个 json 序列化的字符串然后 return 字符串。在这种情况下,Spring 将使用 StringHttpMessageConverter 而不是 JSON 转换器。
@RequestMapping("/api/status")
public Map doSomething()
{
return Collections.singletonMap("status", myService.doSomething());
}
PS。仅适用于 1 个值
你也可以为此使用哈希图
@GetMapping
public Map<String, Object> get() {
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("results", somePOJO);
return map;
}
使用ResponseEntity<ResponseBean>
在这里您可以根据需要使用 ResponseBean 或任何 java bean 来 return 您的 api 响应,这是最佳实践。我已经使用 Enum 进行响应。它将 return 状态代码和 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);
}
用于响应 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 应该有下面的键响应
- 状态码
- 留言
您将在下面得到最终回复
{
"StatusCode" : "0",
"Message":"Login success"
}
您可以根据需要使用 ResponseBody(java POJO, ENUM,etc..)。
更正确地为 API 查询创建 DTO,例如 entityDTO:
- 实体列表的默认响应 OK:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
return entityService.getAllEntities();
}
但如果您需要 return 不同的地图参数,您可以使用接下来的两个示例
2.对于return一个参数如map:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
return ResponseEntity.status(HttpStatus.CREATED).body(
Collections.singletonMap("key", "value"));
}
- 如果您需要 return 某些参数的映射(自 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"));
}
如果您需要使用字符串 return 一个 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);
}
...
}
我使用 return 控制器中的 Map 通过使用 org.json.JSONObject 的 toMap() 方法,如下所示。
@GetMapping("/json")
public Map<String, Object> getJsonOutput() {
JSONObject jsonObject = new JSONObject();
//construct jsonObject here
return jsonObject.toMap();
}
我在 Spring 引导中有一个示例 RestController:
@RestController
@RequestMapping("/api")
class MyRestController
{
@GetMapping(path = "/hello")
public JSONObject sayHello()
{
return new JSONObject("{'aa':'bb'}");
}
}
我正在使用 JSON 库 org.json
当我点击 API /hello
时,出现异常:
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
这是什么问题?有人可以解释到底发生了什么吗?
您当前的方法不起作用的原因是默认情况下使用 Jackson 来序列化和反序列化对象。但是,它不知道如何序列化 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" }
这有点受限,因为添加子对象可能会变得有点困难。 Jackson 有自己的机制,使用 ObjectNode
和 ArrayNode
。要使用它,您必须在 service/controller 中自动装配 ObjectMapper
。然后你可以使用:
@GetMapping
public ObjectNode sayHello() {
ObjectNode objectNode = mapper.createObjectNode();
objectNode.put("key", "value");
objectNode.put("foo", "bar");
objectNode.put("number", 42);
return objectNode;
}
这种方法允许您添加子对象、数组,并使用各种类型。
您可以按照@vagaasen 的建议return 作为String
的响应,或者您可以使用Spring 提供的ResponseEntity
对象,如下所示。这样也可以return 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);
}
}
当您使用 Spring Boot web 时,Jackson 依赖是隐式的,我们不必明确定义。如果使用 eclipse,您可以在依赖层次结构选项卡中的 pom.xml
中检查 Jackson 依赖。
正如您用 @RestController
注释的那样,无需进行显式 json 转换。仅 return 一个 POJO 和 jackson 序列化程序将负责转换为 json。与@Controller 一起使用时相当于使用@ResponseBody
。我们没有在每个控制器方法上放置 @ResponseBody
,而是放置 @RestController
而不是 vanilla @Controller
,默认情况下 @ResponseBody
应用于该控制器中的所有资源。
参考这个 link: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
您遇到的问题是因为 returned 对象(JSON对象)的某些属性没有 getter。你的意图不是序列化这个 JSONObject 而是序列化一个 POJO。所以只是 return POJO。
参考这个 link:
如果你想 return 一个 json 序列化的字符串然后 return 字符串。在这种情况下,Spring 将使用 StringHttpMessageConverter 而不是 JSON 转换器。
@RequestMapping("/api/status")
public Map doSomething()
{
return Collections.singletonMap("status", myService.doSomething());
}
PS。仅适用于 1 个值
你也可以为此使用哈希图
@GetMapping
public Map<String, Object> get() {
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("results", somePOJO);
return map;
}
使用ResponseEntity<ResponseBean>
在这里您可以根据需要使用 ResponseBean 或任何 java bean 来 return 您的 api 响应,这是最佳实践。我已经使用 Enum 进行响应。它将 return 状态代码和 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);
}
用于响应 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 应该有下面的键响应
- 状态码
- 留言
您将在下面得到最终回复
{
"StatusCode" : "0",
"Message":"Login success"
}
您可以根据需要使用 ResponseBody(java POJO, ENUM,etc..)。
更正确地为 API 查询创建 DTO,例如 entityDTO:
- 实体列表的默认响应 OK:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.OK) public List<EntityDto> getAll() { return entityService.getAllEntities(); }
但如果您需要 return 不同的地图参数,您可以使用接下来的两个示例
2.对于return一个参数如map:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Object> getOneParameterMap() { return ResponseEntity.status(HttpStatus.CREATED).body( Collections.singletonMap("key", "value")); }
- 如果您需要 return 某些参数的映射(自 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")); }
如果您需要使用字符串 return 一个 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);
}
...
}
我使用 return 控制器中的 Map
@GetMapping("/json")
public Map<String, Object> getJsonOutput() {
JSONObject jsonObject = new JSONObject();
//construct jsonObject here
return jsonObject.toMap();
}