double-quote to start field name

double-quote to start field name

Unexpected character … was expecting double-quote to start field name


🚨 문제


회사 결제 서비스에서 파트너사의 API를 연동하는 업무를 진행하던 중 발생했다.

대략 아래와 같은 예외 메시지였다.


org.codehaus.jackson.JsonParseException: 
   Unexpected character ('n' (code 110)): was expecting double-quote to start field name
   at [Source: java.io.StringReader@7c87c24a; line: 1, column: 3]


🚧 원인


JSON 필드 네임에 쌍따옴표를 예상했다는 예외 메시지를 보고 파트너사 API의 응답 페이로드를 확인해봤다.

JSON 응답 필드에 홀따옴표가 사용되고 있었다.

근데, 나는 JSON 필드를 쌍따옴표로 쓰긴 하지만 홀따옴표로 사용되는것도 몇 번 봤던 기억이 있어서 당연히 되는건줄 알고 있었기 때문에 이 상황이 잘 이해가 안됐었다.

그래서 처음으로 JSON 공식 홈페이지 에 들어가서 스펙을 확인해봤다.

A value can be a string in double quotes, or a number, or true or false or null, or an object or an array. These structures can be nested.

JSON 스펙상으로 홀따옴표가 아닌 쌍따옴표롤 써야 한다고 나와있었다.

즉, 홀따옴표는 비표준이다.


하지만, 다른 프로젝트에서는 JSON 파서가 홀따옴표로 문제없이 처리해내던 기억이 나서 호기심에 이 부분도 한번 찾아보았다. (굳이 찾아보지 않아도 대략 짐작은 갔지만…)

내가 사용하는 라이브러리는 Jackson이며, ObjectMapper를 주로 사용한다.

📜 jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES


Feature that determines whether parser will allow use of single quotes (apostrophe, character ‘'’) for quoting Strings (names and String values). If so, this is in addition to other acceptable markers. but not by JSON specification).

Since JSON specification requires use of double quotes for field names, this is a non-standard feature, and as such disabled by default.


역시나 관련 내용이 있었다.

해당 사항은 표준이 아니기 때문에 때문에 기본적으로 비활성화 해두었다고 명시돼있었다.

아마 JSON 으로 데이터를 주고받을 때 홀따옴표를 사용하면 이를 모두 쌍따옴표로 변환하는 작업이 수반될텐데, 이를 모두 정규식으로 처리하기 때문으로 의심이 갔다.

주고 받는 데이터의 규모가 크다면 이는 불필요한 작업이면서도(애초에 표준을 지켰다면 필요하지 않은 비용이기 때문이다), 무시하기 힘든 비용이 발생할것이기 때문이다.

즉, 표준을 지켜 개발하라는 의미이며 이는 두말할나위 없는 정론이다.


종국엔 혹시나 표준을 지키지 않는 상황마저 가정해 이러한 옵션을 넣어놓은 개발자들에게 감탄마저 나왔다.


✅ 해결


우선 파트너사에 연락하여 위 내용들을 설명함과 동시에 표준을 지켜 개발해달라 요청하였고, 이 요청은 받아들여졌다.

하지만 다른 API도 홀따옴표를 사용하고 있을 수 있고, 파트너사측 개발자분이 수정하실 때 누락될수도 있는 여지가 있기 때문에 ObjectMapper 설정에 홀따옴표를 허용하는 기능을 활성화해주었다.


@Configuration
public class ObjectMapperConfig implements Jackson2ObjectMapperBuilderCustomizer {
    @Override
    public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
        jacksonObjectMapperBuilder
                .modules(new JavaTimeModule())
                .visibility(PropertyAccessor.FIELD, Visibility.ANY)
                .featuresToEnable(JsonParser.Feature.ALLOW_SINGLE_QUOTES) // 추가 !
                .featuresToDisable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
                .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .build();
    }
}



© 2022. All rights reserved.