관리 메뉴

제뉴어리의 모든것

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 297 path $ 에러 본문

BugNote

Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 297 path $ 에러

제뉴어리맨 2023. 4. 5. 17:21

상황

클라이언트에게 전달되는 DTO 안에 LocalDateTime 타입의 변수가 존재하였다.

그리고 해당 DTO를 넘겨받은 클라이언트에서는 문제없이 잘 작동하여 페이지까지 잘 로딩을 시켰다.

하지만, Test 중에 해당 DTO를 다시 객체화하는 Deserialize(역직렬화) 과정에서 에러가 발생하였다.

 

에러 발생 코드는 아래와 같다.

SingleResponseDto<NewsDto.Response> response = gson.fromJson(actions.andReturn().getResponse().getContentAsString(), new TypeToken<SingleResponseDto<NewsDto.Response>>(){}.getType());

 

서버로 부터 넘겨 받은 Json 데이터를 역직렬화 하는데 사용할 라이브러리는 Gson 이며,

DTO의 구조는 다음과 같다.

 

SingleResponseDto

public class SingleResponseDto <T>{

    int status = 200;
    T data;
}

 

NewsDto.Response

    public static class Response{

        private Long newsId;
        private String memberEmail;
        private String newsTitle;
        private String newsContents;
        private String[] newsTags;
        private List<NewsImageDto.Response> newsImages;
        private LocalDateTime modifiedAt;
    }

 

 

서버에서 넘겨 받은 문제의 Json 데이터는 다음과 같다

{"status":201,"data":{"newsId":23,"memberEmail":null,"newsTitle":"테스트제목","newsContents":"테스트내용입니다10자","newsTags":["test1","test2","test3"],"newsImages":[{"imageId":30,"url":"/imagePath/23/07474ddc-9cab-4918-b815-92a253dbe68a.news-images","newsId":null}],"modifiedAt":[2023,4,5,17,0,51,469654900]}}

위에서 문제인 부분은 바로 LocalDateTime 타입의 데이터인 modifiedAt 부분이다.

보시다시피 배열의 형태로 이상하게 넘어오고 있다.

 

 


에러 메세지

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 297 path $.data.modifiedAt

 


원인

알아본 봐에 의하면 원인은 2가지 이다.

 

1. 기본적으로 Gson은 LocalDateTime을 인식하지 못한다.

2. 넘어 온 Json데이터의 형태가 데이트 타입으로써 알 수 없는 배열로 되어있다. 


해결 방법

 

우선 첫번째로 해본 것은 구글링하여 발견한 방법이다.

에러의 1번째 원인을 해결하기 위한 방법이다.

 

Gson에 LocalDateTime의 파싱 Adapter를 등록한다.

다음과 같이 테스트시에 Gson에게 LocalDateTime 타입의 데이터를 어떻게 파싱할지 알려준다.

    Gson gson;
    
    @BeforeAll
    void beforeAll() throws Exception {

        ...

        //LocalDateTime 타입의 데이터가 json 형태로 넘어 왔을때, 배열 형태로 넘어오는데 해당 데이터를 Gson으로 파싱하여 LocalDateTime 변수에 다시
        //넣으려 할때 배열로 넘어온 LocalDateTime을 Gson이 제대로 인식하지 못해서 에러가 발생한다.
        //그러므로, 다음과 같이 LocalDateTime을 정상적으로 파싱하기위해 Gson에 Adapter를 추가등록한다.
        gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
            @Override
            public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                    throws JsonParseException {
                return LocalDateTime.parse(json.getAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
            }
        }).create();
		
        
        
        ...
        
    }

 

 

하지만 위와 같이 처리를 해주어도

다음과 같이 에러가 발생하였다.

java.lang.IllegalStateException
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException

 

이유는 Gson에게 파싱 방법을 알려주었지만, 애초에 넘어 온 데이터가 이상한 배열 형태로 되어 있다는것이다.

그러므로 애초에 서버에서 넘겨주는 데이터 자체를 LocalDateTime 으로 파싱할 수 있도록 패턴화 하여 넘겨주기로 하였다.

 

서버에서 LocalDateTime 타입의 데이터를 넘겨줄때 LocalDateTime이 인식할 수 있는 패턴의 String 형태로 전달한다

    public static class Response{

        private Long newsId;
        private String memberEmail;
        private String newsTitle;
        private String newsContents;
        private String[] newsTags;
        private List<NewsImageDto.Response> newsImages;
        
        @JsonDeserialize(using = LocalDateDeserializer.class) //json형태로 파싱할때 사용할 클래스
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") //json 형태로 파싱할때 결과물의 데이터 타입, 그리고 파싱 형태 설정
        private LocalDateTime modifiedAt;
    }

 

해당 작업까지 진행하여 최종적으로 에러를 해결되었다.

 


결론

 

1. Gson에 LocalDateTime의 파싱 Adapter를 등록한다.

2. 서버에서 LocalDateTime 타입의 데이터를 넘겨줄때 LocalDateTime이 인식할 수 있는 패턴의 String 형태로 전달한다

 


참조

https://blog.naver.com/PostView.naver?blogId=wideeyed&logNo=222572501692