Development/Daily Error

Cannot find a (Map) Key deserializer for type 해결

📝 작성 : 2022.11.17  ⏱ 수정 : 
반응형

문제

image-20221117150430395

Cannot find a (Map) Key deserializer for type [simple type, com.example.GroupView]

Map의 키를 단순 String이 아닌 클래스로 했더니 Jackson의 역직렬화 중 에러 발생

키로 사용하는 코드는 아래와 같이 간단한 DTO 객체

public class GroupView {

    public GroupView(String uuid, String name, String alias) {
        this.uuid = uuid;
        this.name = name;
        this.alias = alias;
    }

    private final String uuid;
    private final String name;
    private final String alias;
}

원인

에러 메시지에 정확하게 나와 있듯, 해당 타입에 대한 deserializer가 구현되어있지 않기 때문. Serializable을 implements 한다고 해결되지는 않았다.

재밌는건 그 전에 Map의 key로 제법 여러가지 데이터를 담고 있는 enum을 넣었을 때는 아무런 문제가 없었는데, 객체가 들어가니 바로 문제가 생긴 것 이었다.

기본적으로 Jackson은 자바 Map을 JSON 객체로 직렬화 하기 때문에 Map의 key 또한 어떻게든 String으로 직렬화 되어야 한다. 그래서 기본적으로는 문자열, 숫자타입, enum 타입만을 제공하고 그 외의 타입은 오브젝트매퍼가 어찌할 바를 모르는 것 이다.

컴파일 타임에 문제가 되면 참 좋았겠지만, 직렬화를 할 때가 되어서야 비로소 에러가 발생한다.

해결

해결방법은 몇 가지가 있다.

일단 당연하게도 Map에 대한 serializer와 deserializer를 구현해주는 것이 근본적인 해결 방안이 되겠다.

image-20221117152010253

SimpleModule을 상속한 모듈을 작성하여, KeyDeserializer를 등록 한다.

public class CustomJacksonModule extends SimpleModule {

    public CustomJacksonModule() {
        addKeyDeserializer(GroupView.class, new KeyDeserializer() {
            @Override
            public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
                return null;
            }
        });
    }

}

그러고 나서는 모듈을 등록한다.

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new CustomJacksonModule());

하다 보니 이렇게 까지 해야 하나 싶다.

심지어 모듈간 데이터를 주고 받아야하는데 맵의 키로 오브젝트를 줬더니 영 문제가 많다.

결론

Map의 key로 Object가 되어 있는데 그걸 JSON 직렬화 해야 하는 일이 생겼다면 뭔가 잘못 진행되고 있다는 신호로 생각하고 개선해보자. API스펙의 키값에 오브젝트가 들어가야 할 일은 드물 것이다.

References

반응형