티스토리 뷰

개발시 발생한 문제를 개인적으로 다시 반복하지 않기 위해 정리함.


<개요>

매번 HTTP 요청을 통해 받는 JSON String 을 Jackson 의 ObjectMapper 를 통해 Map 의 Key-Value 형태로 변환하여 사용하였다. 

그러다 Object Mapping을 좀 더 자동화하고 필드를 Getter 형태로 편리하게 사용할 수 없을까 고민하다가 사용하게 된 것이

JSON Serialization, Deserialization 이다.


<문제 상황>

가뜩이나 영어 실력도 부족해서 영문서 번역이 오래걸리는데, 일정까지 촉박하여 마음이 급했다. 그래서 

여기저기 다른 블로그나 문서들을 참조하여 이렇게 해보고 저렇게 해보고 대충 끼워맞추기식으로 코딩을 하다보니 

다음과 같은 오류가 발생했다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class net.****.***.svc.**.http.json.model.Time and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: net.****.***.svc.**.http.json.RetrieveTimerInfoResponse["timerlist"]->java.util.ArrayList[0]->net.****.***.svc.**.http.json.model.DeviceTimerSet["timer"]->java.util.ArrayList[0]->net.****.***.svc.**.http.json.model.Timer["time"])
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:569) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:597) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:142) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:100) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:21) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:186) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:569) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:597) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:142) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:100) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:21) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:186) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:569) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:597) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:142) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:118) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2718) ~[jackson-databind-2.2.3.jar:na]
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2210) ~[jackson-databind-2.2.3.jar:na]
cs



<문제 분석>

가장 윗줄만 봐도 알 수 있는 오류였다..

No serializer found for class net.****.***.svc.**.http.json.model.Time and no properties discovered to create BeanSerializer

Time 클래스에서 Serializer 를 찾을 수 없고, BeanSerializer 를 생성하기 위한 프로퍼티를 찾을 수 없었다는 말이다.

Time 클래스는 직접 만든 엔티티클래스인데, 만들 때 ObjectMapper 가 직렬화할 때 필요한 무언가의 설정을 안해줬다는 것이다.


기본적으로 Jackson 2.x 에서 public 필드 또는 getter 메소드가 존재하는 필드에만 동작하게 되어있다.

즉, 저 오류는 필드에 private 걸어놓고 getter 안만들어 놓았다는 소리와 비슷하다. (다른 설정방법도 존재하므로 비슷하다고 표현함)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import com.fasterxml.jackson.annotation.JsonProperty;
 
public class Time {
    
    private String week;
    private String time;
    
    public Time(
            @JsonProperty("week") String week, 
            @JsonProperty("time") String time) {
        this.week = week;
        this.time = time;
    }
 
    //Getter Omitted
}
 
cs


<해결 방법>
1. private 같은거 상관없이 ObjectMapper 차원에서 모든 필드를 감지할 수 있도록 세팅해주기.
: ObjectMapper에 visibility 를 아래와 같이 설정해준다.

1
2
3
4
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

String jsonString = jsonMapper.writeValueAsString(new SomeObjectWithoutGetter());
cs


2. Class 차원에서 ObjectMapper가 필드를 감지할 수 있도록 세팅해주기.

: Class 에 @JsonAutoDetect annotation 을 통해 Visibility 를 아래와 같이 설정해준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class Time {
    
    private String week;
    private String time;
    
    public Time(
            @JsonProperty("week") String week, 
            @JsonProperty("time") String time) {
        this.week = week;
        this.time = time;
    }
}
cs

3. 직접 생성한 엔티티 클래스라면 만들때부터 Getter 를 포함시켜주기.
: 만들 때부터 Getter를 미리 생성해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Time {
    
    private String week;
    private String time;
    
    public Time(
            @JsonProperty("week") String week, 
            @JsonProperty("time") String time) {
        this.week = week;
        this.time = time;
    }
 
    public String getWeek() {
        return week;
    }
 
    public String getTime() {
        return time;
    }
}
 
cs

<결론>

내부적으로 Jackson 이 Object를 Mapping 하면서 각 필드에 대해 Getter 를 활용하는 것으로 보인다.

이는 3과 같이 Getter 메서드를 클래스에서 지원하거나, 

그렇지 않을 경우, ObjectMapper 가 필드들을 잘 탐지할 수 있도록 

1, 2 와 같은 방법을 통해 설정해주어야 정상적으로 Mapping이 작동한다는 것이다.



<참조>

Baeldung - Do JSON right with Jackson : 바로가기

Stackoverflow - Serializing with Jackson (JSON) - getting “No serializer found”? : 바로가기

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함