하나의 특정 실행에 대해 ObjectMapper를 구성 할 수 있습니까?
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);
}
}
일시적으로 설정을 전환하려면 작동합니다. 그러나 매퍼가 비동기 적으로 사용되는 것을 고려할 때 전역 구성을 변경하는 것은 확실히 나쁜 생각입니다. 또한 구성 전환이 이루어질 때까지 매퍼를 잠그는 것도 고려했지만 매퍼가 정적이기 때문에 또 다른 나쁜 생각 일 수 있습니다. 주석이나 매개 변수와 같은 깔끔한 방법이 단일 실행에 마술처럼 영향을 미치고 싶습니다. 가능할까요?
답변
현재 가지고있는 것은 글로벌 매퍼의 구성을 일시적으로 변경하고 있기 때문에 위험합니다. 이것은 동일한 매퍼 인스턴스를 동시에 사용하여 직렬화를 수행하는 다른 스레드에도 영향을 미칩니다.
그러나 필요한 것을 달성하는 또 다른 방법이 있습니다. 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);
}
Map<String, Object>resultMap = getResult();
try {
response = mapper
.writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) { throw new RuntimeException(e);}