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


85

我在Spring Boot中有一个示例RestController:

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

我正在使用JSON库 org.json

当我点击API时/hello,出现一个异常提示:

路径为[]的上下文中的Servlet [dispatcherServlet]的Servlet.service()引发异常[请求处理失败;嵌套异常为java.lang.IllegalArgumentException:未找到类型为org.json.JSONObject]的返回值的转换器,其根本原因是

java.lang.IllegalArgumentException:未找到类型为org.json.JSONObject的返回值的转换器

有什么问题 有人可以解释到底发生了什么吗?


Jackson无法将JSONObject转换为json。
波城

好,我明白了。如何解决这个问题?
iwekesi

1
我希望即时做出回应。我不想为每个响应创建特定的类。
iwekesi

2
只将您的方法返回为String可能会更好。此外,您还可以在方法中添加注释@ResponseBody,这将按要求处理您的响应:-)@GetMapping(path = "/hello") @ResponseBody public String sayHello() {return"{'aa':'bb'}";}
vegaasen

@vegaasen您能详细说明一下ResponseBody
iwekesi

Answers:


101

当您使用Spring Boot Web时,Jackson依赖关系是隐式的,我们不必显式定义。pom.xml如果使用eclipse ,则可以在依赖项层次结构选项卡中检查Jackson依赖项。

并且正如您所注释的那样,@RestController无需进行显式的json转换。只需返回一个POJO,杰克逊序列化器将负责转换为json。@ResponseBody与@Controller一起使用时,等效于使用。而不是放置@ResponseBody在每个控制器方法上,我们放置的@RestController不是香草@Controller,而是@ResponseBody默认情况下应用于该控制器中的所有资源。
请参阅此链接: https : //docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

您面临的问题是因为返回的对象(JSONObject)没有某些属性的吸气剂。您的意图不是序列化此JSONObject而是序列化POJO。因此,只需返回POJO。
请参阅此链接: https : //stackoverflow.com/a/35822500/5039001

如果要返回json序列化的字符串,则只需返回该字符串。在这种情况下,Spring将使用StringHttpMessageConverter而不是JSON转换器。


如果json字符串是您想从Java返回的内容,那么只要已将json序列化,就可以返回一个字符串。无需将字符串转换为json,而将json返回字符串。
prem kumar

5
如果要返回一组没有严格编译时结构的名称/值对,则可以返回一个Map<String,Object>或一个Properties对象
Vihung

@prem kumar random问题:“代替普通的Controller和ResponseBody”是什么意思?这里是什么香草?
Orkun Ozen

我的意思是通常的Controller并在每个请求方法上放置ResponseBody注释。
库尔玛(Prem kumar)'18

65

您当前的方法不起作用的原因是,默认情况下使用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" }

这有点受限制,因为添加子对象可能会变得更加困难。杰克逊使用ObjectNode和拥有自己的机制ArrayNode。要使用它,您必须自动连接ObjectMapper服务/控制器。然后,您可以使用:

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

这种方法允许您添加子对象,数组并使用所有各种类型。


2
这里的映射器是什么?
iwekesi

1
@iwekesi是ObjectMapper应该自动接线的Jackson (请参阅我上一个代码片段上方的段落)。
g00glen00b

1
惊艳知道,一个人去这样的长度生产有意义的JSON对象!同样令人遗憾的是,Pivotal根本没有做出任何努力(spring.io/guides/gs/actuator-service)来指出这些限制。幸运的是,我们有!;)
cogitoergosum

导入java.util.HashMap无法解析
Hikaru Shindo

java.util.HashMap自Java 1.2起,@ HikaruShindo是Java的核心功能。
g00glen00b

43

您可以String按照@vagaasen的建议返回响应,也可以使用ResponseEntitySpring提供的Object,如下所示。通过这种方式,您还可以返回Http status code在Web服务调用中更为有用的内容。

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

1
如果我在实体中添加JSONObject,它将再次给我类似的异常
iwekesi

@ Sangam您的答案帮助我解决了与jackson-dataformat-xml相关的另一个问题
神的神,

这是一个很大的帮助!谢谢!
jones-chris

1
我想知道为什么这个答案没有得到更多支持。我也是Spring的新手,所以我不知道这是否是一个好的软件工程实践。话虽如此,这个答案确实帮助了我。但是,确实给我带来了很多麻烦JSONObject,但是由于Spring使用Jackson,因此我将其更改为a HashMap,然后我在此答案中阅读的代码起作用了。
Melvin Roest

2
+1用于建议MediaType.APPLICATION_JSON_VALUE,因为它可以确保将生成的结果解析为json而不是xml(如果您未定义则可能会发生)
Sandeep Mandori

11

您也可以为此使用哈希表

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

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

PS。仅适用于1个值


3

使用 ResponseEntity<ResponseBean>

在这里,您可以根据需要使用ResponseBean或Any java bean返回api响应,这是最佳实践。我已使用Enum进行响应。它将返回状态代码和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应该具有以下响应键

  1. 状态码
  2. 信息

您将在下面得到最终答复

{

   "StatusCode" : "0",

   "Message":"Login success"

}

您可以根据需要使用ResponseBody(java POJO,ENUM等)。


2

更正确的API查询创建DTO,例如,entityDTO:

  1. 默认响应OK,带有实体列表:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
    return entityService.getAllEntities();
}

但是,如果需要返回不同的Map参数,则可以使用下面的两个示例
2。对于返回一个参数,如map:

@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
    return ResponseEntity.status(HttpStatus.CREATED).body(
            Collections.singletonMap("key", "value"));
}
  1. 并且如果您需要某些参数的返回映射(自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"));
}

1

如果您需要使用String返回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);
    }
    ...
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.