Можно ли настроить ObjectMapper для одного конкретного исполнения?

Я работаю над веб-проектом, в котором используется один статический ObjectMapper, который настраивается через файл XML и должен действовать во всем проекте. Однако мне нужно реализовать 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);
            }
        }

Чтобы временно переключать настройки, работает. но, учитывая, что преобразователь используется асинхронно, менять глобальную конфигурацию — определенно плохая идея. Я также думал о том, чтобы заблокировать маппер до обратного переключения конфигурации, но, поскольку маппер статичен, это может быть еще одной плохой идеей. Я хотел бы иметь какой-нибудь аккуратный способ, например, аннотацию или параметр, волшебным образом влияющий на одно выполнение. Интересно, возможно ли это?


person Jia Fang    schedule 16.11.2020    source источник
comment
Разве вы не можете вместо этого иметь два разных экземпляра ObjectMapper?   -  person sp00m    schedule 16.11.2020
comment
В зависимости от производительности вы также сможете скопировать ObjectMapper stackoverflow.com/a/61614730/5126654. скопируйте маппер, а затем измените конфигурацию.   -  person Stephan Stahlmann    schedule 16.11.2020


Ответы (1)


То, что у вас сейчас есть, опасно, так как вы временно меняете конфигурацию глобального картографа. Это также повлияет на другие потоки, выполняющие сериализацию с использованием одного и того же экземпляра преобразователя в то же время.

Однако есть и другой способ добиться того, что вам нужно. Экземпляр ObjectMapper имеет несколько методов для создания экземпляра ObjectWriter на основе вашего преобразователя.

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 в качестве корня для целей конфигурации и внесите некоторые изменения, чтобы он генерировал JSON, соответствующий API-контракту.

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);
}
person Felix    schedule 16.11.2020