본문 바로가기

코딩/Java, SpringBoot

SpringBoot Map으로 파싱하기. Jackson

@
OAuth를 진행하던 중 아래의 문자열을 파싱해야하는 상황이 왔다.

아래의 형식은 네이버 OAuth 응답 형태임.

아래의 JSON을 어떻게 파싱하나 궁금했다.. 객체로 바인당 하려면 계층 구조 때문에 객체를 2개 정의해야하는 상황임.. 귀찮았다. 그냥 Map으로 받으면 알아서 해주지 않을까? jackson이 어떻게 동작하는지 궁금했다. 

jackson은 스프링 부트에 기본적으로 포함되어 있으므로 별도의 설정을 하지 않아도 된다. 컨트롤러에서 json->object 바인딩을 할 때 Jackson으로 파싱, 바인딩한다.

 

{resultcode=00, message=success, response={id=22323-O8aLaUSxljfFZ52g4, age=20-29, gender=M, email=32131@gmail.com, mobile=010-1234-1234, mobile_e164=+821012341234, name=홍길동, birthday=10-13, birthyear=1111}}

위의 문자열은 RestTemplate을 사용해서 받아온 map.getBody()에 해당한다.(아래코드 참조)

 

@

ResponseEntity<Map> map = Request.restTemplate.exchange(naverMemberInfoURL, HttpMethod.GET, entity, Map.class);
//<String, String>을 지정하면 안된다. 디폴트로 <Object, Object>를 사용하게 한다. 보통 <String, Object>로 사용한다.
log.info(map.getClass().toString()); //class org.springframework.http.ResponseEntity
log.info(map.getBody().getClass().toString()); //class java.util.LinkedHashMap

log.info(map.toString());
log.info(map.getBody().toString());
log.info(map.getBody().keySet().toString());
log.info(map.getBody().get("response").toString());
log.info(map.getBody().get("response").getClass().toString());
log.info(((Map)(map.getBody().get("response"))).get("name").toString());

 

결과는 아래와 같다. 신기한것은 계층구조로 있으면 알아서 Map을 만들어준다는  것임. jackson 굿!

Map<String, String>으로 고정시키면 오히려 에러난다. map->string이 안된다고 에러남.! 보통 Map<String, Object>로 사용한다.

 

map.toString() : <200,{resultcode=00, message=success, response={id=51ICfhCQb2yZ5P8B2zR1XrACj-123123, age=20-29, gender=M, email=1234@gmail.com, mobile=010-3391-6486, mobile_e164=+821033916486, name=홍길동, birthday=10-15, birthyear=1111}},[Server:"nginx", Date:"Tue, 19 Jul 2022 01:08:02 GMT", Content-Type:"application/json; charset=utf-8", Content-Length:"281", Connection:"keep-alive", Keep-Alive:"timeout=5", Vary:"Accept-Encoding", apigw-uuid:"02e50198-0eba-428e-8db1-b004215c7aa8", Cache-Control:"no-cache, must-revalidate"]>


map.getBody().toString() : {resultcode=00, message=success, response={id=51ICfhCQb2yZ5P8B2zR1XrACj-O8aLaUSxljfFZ52g4, age=20-29, gender=M, email=123124@gmail.com, mobile=010-1234-1234, mobile_e164=+821012341234, name=홍길동, birthday=10-15, birthyear=1111}}

map.getBody().keySet().toString() : [resultcode, message, response]

 

map.getBody().get("response").toString() :{id=2323-O8aLaUSxljfFZ52g4, age=20-29, gender=M, email=zhdhfhd33@gmail.com, mobile=010-1234-1234, mobile_e164=+821012341234, name=홍길동, birthday=10-15, birthyear=1111}

 

map.getBody().get("response").getClass().toString()

map.getBody().get("response").getClass().toString() : class java.util.LinkedHashMap

계층구조도 자동으로 LinkedHashMap을 사용해서 바인딩 해준다. getClass()는 타입이 아니라 실제로 저장된 객체의 클래스를 반환해준다. Object 타입으로 LinkedHashMap객체가 있으면 Object가 아니라 LinkedHashMap을 반환함.

 

((Map)(map.getBody().get("response"))).get("name").toString() : 홍길동

Map으로 캐스팅이 필요함. Object타입을 반환하기 때문임.

 

처음 map에는 resultcode, message, response가 있다. response안에 다시 객체가 있어서 이를 바인딩 해야하나 당황스러웠다. 처음에 Map으로 받아오니까 알아서 map.getBody().get("response")의 타입도 Map으로 설정해주었다. 고민하지말고 map사용하자!

일단 RestTemplate 사용할 때는 그렇다... RestTemaplate 내부적으로 어떤 라이브러리를 사용하는지 모르겠다. Jackson에서도 똑같이 되는지 알아보자.

 

@

실험 해보니까 Jackson ObjectMapper도 위와 같이 계층구조일 때 알아서 바인딩 할 수 있다. 그런데 =가 아니라 : 로 바꿔줘야한다. 그리고 모든 key-value에 "를 이스케이프 시켜서 붙여야한다...

RestTemplate에서 사용하는 파서가 더 좋은데??....

그리고 주의할점이 Map<String, String>으로 고정시키면 안된다. 제네릭 없이  Map을 사용하거나 (Map<Object, Object>), Map<String, Object>를 사용해야함!

@Test
public void JSONparesTest() {
    String json = "{\"resultcode\":\"00\", \"message\":\"success\", \"response\":{\"id\":\"asdfasdga-O8aLaUSxljfFZ52g4\", \"age\":\"20-29\"}}";
    Map map;
    try {
        map = Mapper.objectMapper.readValue(json, Map.class);
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
    System.out.println(map.get("response").getClass());

}