본문 바로가기

Java

Java - Json(ObjectMapper) 배열(Collection) 타입이지만, 데이터가 1개인 경우 Object로 작성되는 배열 필드 처리하기

최근 공공 API를 이용해 데이터를 수집하는 어플리케이션을 개발했고, 테스트 후 실제 데이터를 처리하기 위해 어플리케이션을 가동 했다.

특별한 문제 없이 잘 동작할 거라고 생각했지만 이상한 에러를 발견했고 (사실 이 전에도 숫자 타입인데 문자열이 튀어나와서 오류가 발생하는 등의 자질구레한 문제가 있었다.) 
원인을 파악 해 봤는데....

원래 아래와 같이 item 필드는 배열로 결과가 나와야 한다.

{"Response": {
    "head": {
        "pageNo": "1",
        "resultCode": "0",
        "totalCount": "17",
        "numOfRows": "1000",
        "resultMsg": "NORMAL_SERVICE"
    },
    "items": {"item": [
        {
            "tong": "",
            "male50AgeNmprCnt": "735632",
            "feml40AgeNmprCnt": "734484",
            "male80AgeNmprCnt": "128005",
            ...
        },
        {...},
        ...
    ]}
}}

 

그런데 결과가 1건인 경우 배열로 나오지 않고 그냥 Object 형태로 제공 하고 있었다.

{
  "Response": {
    "head": {
      "pageNo": "1",
      "resultCode": "0",
      "totalCount": "1",
      ...
    },
    "items": {
      "item": {
        "tong": "11",
        "male50AgeNmprCnt": "4",
        "feml40AgeNmprCnt": "3",
        "male80AgeNmprCnt": "0",
        "male20AgeNmprCnt": "0",
        ...
        }
    }
  }
}

 

왜 저렇게 결과를 제공하는지 잘 모르겠지만 API를 고쳐달라고 할 수 없으니 해결 방법을 찾아봤다.

다행히 ObjectMapper 에는 직렬화나 역직렬화(Serialization, Deserialization) 작업시 다양한 옵션을 적용할 수 있었다.

옵션을 적용하는 방법은 enable() 함수에 enum 타입의 SerializationFeature 또는 DeserializationFeature를 전달하여 적용한다.

ObjectMapper om = new ObjectMapper();
om.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

위에서 적용한 DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY 를 적용하면 단일 값을 배열로 처리가 가능해져서 맨 위에서 발생했던 오류를 해결 할 수 있다.

(해당 기능 말고도 더 다양한 기능들이 있으니 SerializationFeature, DeserializationFeature를 참고 하자)

 

그런데 API 호출을 OpenFeign을 이용하다보니 ObjectMapper를 사용하지 않아 위의 해결 방법을 사용 할 수 없었다.

그래서 다른 방법을 찾던중 @JsonFormat 어노테이션의 with 속성을 이용해서 위 기능을 구현 할 수 있는 것을 발견했다. with 속성은 enum 타입의 JsonFormat.Feature를 매개변수로 받으며, 사용법은 아래와 같다

@Getter
@Setter
public static class Response {
    private Head head;
    private Items items;

    public Response() {
    }
    public Response(Head head, Items items) {
        this.head = head;
        this.items = items;
    }
}

@Getter
@Setter
@NoArgsConstructor
public static class Items {

    @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
    private List<Item> item;
    
}

 

ObjectMapper 에 기능을 적용하면 모든 필드에 적용 되지만 어노테이션을 이용하면 문제가 될만한 특정 필드에만 해당 기능을 적용 할 수 있게 되어 예상치 못한 동작을 방지 할 수 있을 것 이라고 생각 한다.

 

반응형