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

时间:2017-06-30 06:14:17

标签: java json spring-boot

我在Spring启动时有一个示例Rest Controller:

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

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


}

我正在使用json库:org.json

当我点击api /你好时,我得到一个例外说:

  

用于servlet [dispatcherServlet]的Servlet.service()与path的上下文   []引发异常[请求处理失败;嵌套异常是   java.lang.IllegalArgumentException:找不到返回的转换器   type of value:class org.json.JSONObject] with root cause

     

java.lang.IllegalArgumentException:找不到返回的转换器   type的值:class org.json.JSONObject

问题是什么?有人可以解释究竟发生了什么。我是SpringBoot的新手。

提前致谢:)

8 个答案:

答案 0 :(得分:53)

当您使用spring boot web时,jackson依赖是隐式的,我们不必明确定义。如果使用eclipse,您可以在依赖关系层次结构选项卡中检查pom.xml中的jackson依赖项。

由于您使用@RestController进行了注释,因此无需进行显式的json转换。只需返回一个POJO和jackson序列化器将负责转换为json。与@Controller一起使用时,它等同于使用@ResponseBody。我们放置@ResponseBody而不是@RestController放置@Controller而不是vanilla @ResponseBody <div class="content"> <form onsubmit="return false"> <div id="speed"> <h3>How fast is your transfer speed?</h3> <p> <label for="internetSpeed">File Transfer Speed</label> <input id="internetSpeed_p" type="button" value="+" onclick="internetSpeed.value = (parseInt(internetSpeed.value)+1).toFixed(2)"> <input id="internetSpeed" type="number" min="0" step="0.01" value="10" placeholder="File Transfer speed" autofocus> <input id="internetSpeed_p" type="button" value="-" onclick="internetSpeed.value = (parseInt(internetSpeed.value)-1).toFixed(2)"> <select id="internetSpeedBase"> <option value="10^3">Kbps</option> <option value="10^6" selected>Mbps</option> <option value="10^9">Gbps</option> </select> </p> <div> <label class="collapse" for="_1">?</label> <input id="_1" type="checkbox"> <div> <p class="overhead-p"><label for="internetOverhead">Overhead</label> <select id="internetOverhead"> <option value="1" selected>None</option> <option value="0.99">1%</option> <option value="0.95">5%</option> <option value="0.9">10% (Typical TCP overhead)</option> <option value="0.85">15%</option> <option value="0.8">20%</option> <option value="0.75">25%</option> <option value="0.7">30%</option> <option value="0.6">40%</option> <option value="0.5">50%</option> <option value="0.4">60%</option> <option value="0.3">70%</option> <option value="0.2">80%</option> <option value="0.1">90%</option> <option value="0.05">95%</option> <option value="0.01">99%</option> </select> </p> </div> </div> </div> <div id="file"> <h3>What is the size of the file you want to transfer?</h3> <p> <label for="fileSize">File Size</label> <input id="internetSpeed_p" type="button" value="+" onclick="fileSize.value = (parseInt(fileSize.value)+1).toFixed(2)"> <input type="number" id="fileSize" value="1" step="0.01" min="0"> <input id="internetSpeed_p" type="button" value="-" onclick="fileSize.value = (parseInt(fileSize.value)-1).toFixed(2)"> <select id="fileSizeBase"> <option value="2^10">KB </option> <option value="2^20">MB </option> <option value="2^30" selected>GB </option> <option value="2^40">TB </option> </select> </p> <p> <button id="calculateButton" >Calculate</button> </p> </div> </form> <div id="results"> <div id="results_inner"> <p class="">It would take</p> <p class="time">0</p> <p class="">to transfer <span class="size"></span> </p> <p class="">at <span class="speed"></span>/sec</p> </div> <hr> </div> 默认情况下应用于该控制器中的所有资源。
请参阅此链接: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

您遇到的问题是因为返回的对象(JSONObject)没有某些属性的getter。并且您的目的不是序列化此JSONObject,而是序列化POJO。所以只需返回POJO。
请参阅此链接: https://stackoverflow.com/a/35822500/5039001

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

答案 1 :(得分:29)

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

这有点受限,因为添加子对象可能会变得有点困难。杰克逊有自己的机制,使用ObjectNodeArrayNode。要使用它,您必须在服务/控制器中自动装配ObjectMapper。然后你可以使用:

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

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

答案 2 :(得分:24)

您可以按照@vagaasen的建议将响应返回为String,也可以使用Spring提供的ResponseEntity对象,如下所示。通过这种方式,您还可以返回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);
    }
}

答案 3 :(得分:7)

您也可以为此

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

答案 4 :(得分:3)

{{1}}

PS。仅适用于1个值

答案 5 :(得分:2)

针对API查询的更正确的创建DTO,例如EntityDTO:

  1. 默认响应好,并带有实体列表:
df = pd.read_csv('https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv',parse_dates=['date'])
df.groupby(['state','date'])[['cases']].mean().reset_index()

但是如果您需要返回不同的Map参数,则可以使用下面的两个示例
2.要返回一个像map这样的参数:

@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
    return entityService.getAllEntities();
}
  1. 如果需要返回某些参数的映射(自Java 9开始):
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
    return ResponseEntity.status(HttpStatus.CREATED).body(
            Collections.singletonMap("key", "value"));
}

答案 6 :(得分:1)

如果您需要使用字符串返回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);
    }
    ...
}

答案 7 :(得分:0)

使用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等)。