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같은 예외가 발생합니다.

[] 경로가있는 컨텍스트의 서블릿 [dispatcherServlet]에 대한 Servlet.service ()에서 예외가 발생했습니다. [요청 처리에 실패했습니다. 중첩 된 예외는 java.lang.IllegalArgumentException : 근본 원인이있는 클래스 org.json.JSONObject] 유형의 리턴 값에 대한 변환기를 찾을 수 없습니다.

java.lang.IllegalArgumentException : 반환 값 유형 : class org.json.JSONObject에 대한 변환기를 찾을 수 없습니다.

이슈가 뭐야? 누군가 정확히 무슨 일이 일어나고 있는지 설명 할 수 있습니까?


Jackson은 JSONObject를 json으로 변환 할 수 없습니다.
Pau

알겠습니다. 이 문제를 해결하려면 어떻게해야합니까?
iwekesi

1
응답이 즉석에서 구성되기를 바랍니다. 각 응답에 대해 특정 클래스를 만들고 싶지 않습니다.
iwekesi

2
메서드가 String으로 반환되도록하는 것이 더 나을 수 있습니다. 또한 메서드에 @ResponseBody 주석을 추가 할 수도 있습니다. 요청 된대로 응답을 처리합니다. :-)@GetMapping(path = "/hello") @ResponseBody public String sayHello() {return"{'aa':'bb'}";}
vegaasen

@vegaasen 당신은위한 responseBody에 대해 조금 자세히 설명 할 수
iwekesi

답변:


102

Spring Boot 웹을 사용하고 있으므로 Jackson 종속성은 암시 적이며 명시 적으로 정의 할 필요가 없습니다. pom.xmlEclipse를 사용하는 경우 종속성 계층 구조 탭에서 Jackson 종속성을 확인할 수 있습니다 .

주석을 달았 @RestController으므로 명시적인 json 변환을 수행 할 필요가 없습니다. POJO를 반환하면 jackson serializer가 json으로의 변환을 처리합니다. @ResponseBody@Controller와 함께 사용할 때 사용 하는 것과 같습니다 . 오히려 배치하는 것이 @ResponseBody모든 컨트롤러 방식에 우리는 배치 @RestController바닐라 대신 @Controller하고 @ResponseBody기본적으로 해당 컨트롤러의 모든 자원에 적용됩니다.
이 링크를 참조하십시오 : 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은 JSON 변환기 대신 StringHttpMessageConverter를 사용합니다.


json 문자열이 Java에서 반환하려는 경우 이미 json이 직렬화되어 있으면 문자열을 반환 할 수 있습니다. 문자열을 json으로 변환하고 json을 다시 문자열로 변환 할 필요가 없습니다.
프렘 쿠마르

5
엄격한 컴파일 타임 구조없이 이름-값 쌍 집합을 반환하려면 Map<String,Object>또는 Properties객체를 반환 할 수 있습니다.
Vihung

@prem kumar 무작위 질문 : '바닐라 컨트롤러 및 ResponseBody 대신'이란 무엇을 의미합니까? 여기 바닐라가 뭐야?
Orkun Ozen

나는 일반적인 컨트롤러와 모든 요청 방법에 ResponseBody 주석을 배치했습니다.
prem kumar

66

현재 접근 방식이 작동하지 않는 이유는 Jackson이 기본적으로 개체를 직렬화 및 역 직렬화하는 데 사용되기 때문입니다. 그러나 .NET Framework를 직렬화하는 방법을 모릅니다 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
여기 매퍼는 무엇입니까?
iwekesi

1
@iwekesi는 ObjectMapper자동 연결해야하는 Jackson입니다 (마지막 코드 스 니펫 위의 단락 참조).
g00glen00b

1
의미있는 JSON 객체를 생성하기 위해 이러한 길이를 사용해야한다는 사실 은 놀랍 습니다! Pivotal이 이러한 한계를 밝히기 위해 전혀 노력 하지 않는 것도 안타깝 습니다 ( spring.io/guides/gs/actuator-service ). 다행히도 SO가 있습니다! ;)
cogitoergosum

가져 오기 java.util.HashMap을 확인할 수 없습니다
Hikaru Shindo

@HikaruShindo java.util.HashMap는 Java 1.2 이후 Java의 핵심 기능입니다.
g00glen00b

43

String@vagaasen에서 제안한 대로 응답을 반환 하거나 ResponseEntity아래와 같이 Spring에서 제공 하는 Object 를 사용할 수 있습니다 . 이 방법 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);
    }
}

1
내가 엔티티에 된 JSONObject를 추가하는 경우, 다시 나에게 유사한 예외주고있다
iwekesi

@Sangam 귀하의 답변은 jackson-dataformat-xml과 관련된 또 다른 문제에 대해 저를 도왔습니다
divine

이것은 큰 도움이되었습니다! 감사합니다!
jones-chris

1
이 답변이 더 이상 찬성되지 않는 이유가 궁금합니다. 저도 Spring을 처음 사용하므로 이것이 좋은 소프트웨어 엔지니어링 관행인지 모르겠습니다. 그렇게 말하면서이 대답은 정말 도움이되었습니다. 그러나, 나는이 가진 많은 문제를 가지고 있었다 JSONObject,하지만 봄이 사용하기 때문에 잭슨 나는 그것을 변경 HashMap이 답변이 작업을 한 내가 읽고 대신 다음 코드입니다.
Melvin Roest

2
이 보장으로 그 생산 결과를 MediaType.APPLICATION_JSON_VALUE을 제안 +1은 사용자가 정의하지 않는 경우에 발생할 수 있습니다로 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());
}

추신. 1 개 값에 대해서만 작동합니다.


3

사용하다 ResponseEntity<ResponseBean>

여기에서 api 응답을 반환하기 위해 ResponseBean 또는 Any java bean을 사용할 수 있으며 이것이 모범 사례입니다. 응답을 위해 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

문자열을 사용하여 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);
    }
    ...
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.