Controller 에서 response 값에 null 값은 어떻게 처리 할까? Gson의 toJson & toJsonTree
프론트 : null인 값도 보내주시면 안될까요? 나 : 음 그렇게 보낸거 같은데요,,? 아차,,, 뭐지? 왜 없지?
오늘 이런 상황이였다. 뭐 이런 null에 대한 처리는 프론트, 백앤드 협의에 따라 누가 처리하냐가 변경될수는 있지만, 난 굉장히 쉬운 문제라 생각하여, 내가 처리키로 하였다. 그런데,, 생각보다 쉽지 않은데? -0-;;
분명 Gson으로 serialize 할때까지는 분명 null 이 있는걸 확인하였는데, 어디서 사라지는 문제일까? 또 그럼 null 이 포함되게 하려면 어떻게 할 수 있을까?
그래서 확인해 보았다.
{
"description" : "아래 key에 value가 없더라도 값을 response해주세요"
// "key" : null
}
// 위같이 null이 안보인다...
0.Test로 사용한 TestDto
@AllArgsConstructor
public class TestDto {
private String value1;
private Integer value2;
private TestChild testChild;
private String nullValue1;
private Integer nullValue2;
private TestChild nullTestChild;
@AllArgsConstructor
public static class TestChild {
private String child1;
}
}
1-1.Case1 단순 스트링에 대한 Return
@RestController
@RequestMapping("test")
public class TestContorller {
/**
* 단순 스트링에 대한 리턴
* @return
*/
@PostMapping("return/string")
public String test() {
TestDto testDto = new TestDto("한승우", 1, new TestDto.TestChild("테스트")
, null, null, null);
Gson gson = new Gson().newBuilder().serializeNulls().create();
String testJson = gson.toJson(testDto);
return testJson;
}
}
1-2. 1에 대한 response
{
"value1": "한승우",
"value2": 1,
"testChild": {
"child1": "테스트"
},
"nullValue1": null,
"nullValue2": null,
"nullTestChild": null
}
null값이 확인된다.
2-1 Gson toJson을 통해 serialize한 값을 담아 Return
public class TestContorller {
@PostMapping("return/responseentity/tojson")
public ResponseEntity<?> test2() {
TestDto testDto = new TestDto("한승우", 1, new TestDto.TestChild("테스트")
, null, null, null);
Gson gson = new Gson().newBuilder().serializeNulls().create();
String testJson = gson.toJson(testDto);
return ResponseEntity.status(HttpStatus.OK).body(testJson);
}
}
2-2. 1에 대한 response
{"value1":"한승우","value2":1,"testChild":{"child1":"테스트"},"nullValue1":null,"nullValue2":null,"nullTestChild":null}
정상적으로 null이 출력되는걸 확인할 수 있다.
3-1 !!문제의!! Gson toJsonTree을 통해 serialize한 값을 담아 Return
public class TestContorller {
@PostMapping("return/responseentity/tojsonTree")
public ResponseEntity<?> test2() {
TestDto testDto = new TestDto("한승우", 1, new TestDto.TestChild("테스트")
, null, null, null);
Gson gson = new Gson().newBuilder().serializeNulls().create();
JsonElement testJson = gson.toJsonTree(testDto);
return ResponseEntity.status(HttpStatus.OK).body(testJson);
}
}
3-2 3-1에 대한 결과
{
"value1": "한승우",
"value2": 1,
"testChild": {
"child1": "테스트"
}
}
보는것 처럼 null에 대한 값이 사라졌다.. 뭐지 분명
Gson gson = new Gson().newBuilder().serializeNulls().create();
를 통해 null값또한 serialize 하기로 하였는데 -0-;; 왜 무시해버리는거냐,,
4. toJson VS toJsonTree
위의 문서를 확인하고 오면 내가 잘못 사용하고 있다는것을 알게 되었다. (난 그냥 serialize 하는게 목적이였는데,,)
Gson.toJson 는 객체를 JsonString 으로 직렬화 하기 위한 단순 메서드 이다.
Json 데이터를 다른곳으로 보내고 저장하는데 유용하다.
가장 일반적으로 사용되는 방법
Gson.toJsonTree , 위 toJson과 같이 객체를 직렬화 변환해주는건 같지만, 이건 JsonElement로 리턴한다.
JsonObject에 추가하기 전에 유용하게 사용할수 있다. 예를 들어 하나의 Json 객체에 병합하려는 두개의 객체가 있는 경우 둘다 JsonObject 로 직렬화한 다음 한 객체의 멤버를 다른 객체로 복사를 할 수 있다.
그러니깐 나는 status랑 user 정보랑 content즉 프론트에 넘겨줄 dto를 jsonObject로 합쳐서 보내줄때, jsonElement로 변환될때 이러한 문제가 생긴것이였다.
(210921 수정) 다시 보니, 해결책을 찾던중 위의 문제가 아니였다. 문제는 JsonObject가 body에 담기고 나면 null 인 값은 삭제가 되는 문제가 발생,
SysOut을 찍어보면 알수 있다.
5. 원인 !!!
public class TestContorller {
@PostMapping("return/responseentity/tojsonTree")
public ResponseEntity<?> test2() {
TestDto testDto = new TestDto("한승우", 1, new TestDto.TestChild("테스트")
, null, null, null);
Gson gson = new Gson().newBuilder().serializeNulls().create();
JsonObject jsonObject = new JsonObject();
JsonElement response1 = gson.toJsonTree(testDto);
JsonElement response2 = gson.toJsonTree(new User("한승우"));
jsonObject.add(response1);
jsonObject.add(response2);
System.out.println(jsonObject); //확인해보면 null값을 가지고 있다.
return ResponseEntity.status(HttpStatus.OK).body(jsonObject); // 결국 여기서 문제가 발생
}
}
6. 해결 방법? !!
해결책??이라 보기는 어려울수도 있지만 결국엔 body에 담을때 jsonObject객체로 담는것이 아닌, jsonString 으로 담아야 한다는것이다.
//public class TestContorller {
// @PostMapping("return/responseentity/tojsonTree")
// public ResponseEntity<?> test2() {
// TestDto testDto = new TestDto("한승우", 1, new TestDto.TestChild("테스트")
// , null, null, null);
// Gson gson = new Gson().newBuilder().serializeNulls().create();
// JsonObject jsonObject = new JsonObject();
// JsonElement response1 = gson.toJsonTree(testDto);
// JsonElement response2 = gson.toJsonTree(new User("한승우"));
//
// jsonObject.add(response1);
// jsonObject.add(response2);
// System.out.println(jsonObject); //확인해보면 null값을 가지고 있다.
//
// return ResponseEntity.status(HttpStatus.OK).body(jsonObject.toString); // 이 부분 확인!
// }
//}
0924 생일날! 해결책 발견
serialize를 2번해야되는 문제이다. gson with spring config 위 링크에서 확인 가능
# 스프링부트 default는 jackson
spring.mvc.converters.preferred-json-mapper=gson
# Whether to serialize null fields.
spring.gson.serialize-nulls= true
gson 에서 한번 serialize 하고, spring 자체에서도 serialize 를 해주어야 한다. ㄷㄷ… 2번이면 어떻게 아냐고 -0-;; 아무튼 알게되어 happy