하나의 특정 실행에 대해 ObjectMapper를 구성 할 수 있습니까?

Nov 16 2020

XML 파일을 통해 구성되고 전체 프로젝트에 적용되는 정적 ObjectMapper 하나를 사용하는 웹 프로젝트를 작업 중입니다. 그러나 설정에 관계없이 null 속성이 무시되지 않는 응답을 보내는 API를 구현해야합니다. 내 상사는 다른 ObjectMapper가 생성되는 것을 원하지 않는다고 말했으며 내 JSON 작성기를 만드는 것은 중복으로 간주되므로 금지되어 있습니다. 그래서 여기에 갇혔습니다. 나는 이것을 시도했다.

        Map<String, Object>resultMap = getResult();
        try {
            mapper.setSerializationInclusion(Include.ALWAYS);
            response = mapper.writeValueAsString(resultMap);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        } finally {
            if (ServiceConfig.isWriteNull()) {
                mapper.setSerializationInclusion(Include.ALWAYS);
            } else {
                mapper.setSerializationInclusion(Include.NON_NULL);
            }
        }

일시적으로 설정을 전환하려면 작동합니다. 그러나 매퍼가 비동기 적으로 사용되는 것을 고려할 때 전역 구성을 변경하는 것은 확실히 나쁜 생각입니다. 또한 구성 전환이 이루어질 때까지 매퍼를 잠그는 것도 고려했지만 매퍼가 정적이기 때문에 또 다른 나쁜 생각 일 수 있습니다. 주석이나 매개 변수와 같은 깔끔한 방법이 단일 실행에 마술처럼 영향을 미치고 싶습니다. 가능할까요?

답변

4 codeflush.dev Nov 16 2020 at 12:12

현재 가지고있는 것은 글로벌 매퍼의 구성을 일시적으로 변경하고 있기 때문에 위험합니다. 이것은 동일한 매퍼 인스턴스를 동시에 사용하여 직렬화를 수행하는 다른 스레드에도 영향을 미칩니다.

그러나 필요한 것을 달성하는 또 다른 방법이 있습니다. ObjectMapper인스턴스는 만들 수있는 여러 가지 방법이 ObjectWriter당신의 매퍼에 따라 -instance합니다.

Map<String, Object> resultMap = getResult();
try {
    response = mapper
        .writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
        .writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
    throw new RuntimeException(e);
}

@Stepan Stahlmann귀하의 의견에서 언급 했듯이 메서드 ObjectMapper를 사용하여 전역 인스턴스를 기반 으로 임시 새 인스턴스를 만들 수도 있습니다 ObjectMapper#copy(). 아이디어는 동일합니다. ObjectMapper구성 목적을 위해 전역 을 루트로 사용하고 API- 계약에 맞는 JSON을 생성하도록 약간 조정합니다.

Map<String, Object> resultMap = getResult();
try {
    response = mapper
        .copy()
        .setSerializationInclusion(Include.ALWAYS)
        .writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
    throw new RuntimeException(e);
}

다른 접근 방식 ...

제가 생각할 수있는 또 다른 방법이 있으며 더 많은 것이 있다고 확신합니다. resultMap매퍼의 기본 동작을 무시해야하는 몇 가지 주석으로 클래스를 래핑 할 수 있습니다 .

package example;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Map;

// Your inclusion rule
@JsonInclude(JsonInclude.Include.ALWAYS)
public class ResponseWrapper {

    private final Map<String, Object> response;

    public ResponseWrapper(Map<String, Object> response) {
        this.response = response;
    }

    // tells Jackson to use the this a the actual value (so you don't see this wrapper in the json)
    @JsonValue
    public Map<String, Object> getResponse() {
        return this.response;
    }
}

Map<String, Object> resultMap = getResult();
try {
    response = mapper.writeValueAsString(new ResponseWrapper(resultMap));
} catch (JsonProcessingException e) {
    throw new RuntimeException(e);
}
yasserRida Nov 16 2020 at 12:29
    Map<String, Object>resultMap = getResult();
         try {
            response = mapper
                .writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
                .writeValueAsString(resultMap);
         } catch (JsonProcessingException e) { throw new RuntimeException(e);}