Итак, вот мои дальнейшие мысли. Ответ Кирилла Симонова является правильным и безопасным с точки зрения типов (альтернативой может быть создание экземпляра аннотации с использованием отражения Kotlin, но это не безопасно).
Вот некоторые проблемы с моим исходным кодом и мысли об исходном подходе:
- Вы должны последовательно переопределить
_hasAnnotation
и _getAnnotation
Вы не можете быть уверены, что _getAnnotation
будет вызван после проверки _hasAnnotation
. Вы не можете быть уверены, какой из них будет использоваться для проверки замененной аннотации (в моем случае @JsonIgnore
), не просматривая код JacksonAnnotationIntrospector
. Кажется, что их постоянное переопределение было бы хорошей практикой. Поэтому мы должны также добавить следующее переопределение в наш класс, если мы хотим использовать этот подход:
override fun <A : Annotation> _hasAnnotation(
annotated: Annotated,
annoClass: Class<A>
): Boolean {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
return true
}
return super._hasAnnotation(annotated, annoClass)
}
_getAnnotation
тип возвращаемого значения должен быть обнуляемым Это было исправлено Кириллом, но не было явно указано. _getAnnotation
может и иногда возвращает ноль.
Вы (возможно) не можете иметь одну магическую аннотацию MyConditional
. Ответ Кирилла может побудить вас создать нечто вроде условной аннотации для всех аннотаций Джексона, которая могла быиспользовать следующим образом:
@MyConditional([
JsonIgnore, JsonProperty("propertyName")
])
У вас просто не может быть параметра полиморфной аннотации. Вам нужно будет создать My*
для каждой необходимой вам аннотации Джексона, а для аннотации с параметрами она будет не такой аккуратной, как с @MyJsonIgnore
.
Вы можете попытаться сделать повторяемую аннотацию, которая будет применятьсякак показано ниже и реализовано с использованием отражения.
@MyConditional(
clazz = JsonProperty::class.java,
args = [
// Ah, you probably cannot have an array of any possible args here, forget it.
]
)
_hasAnnotation
и _getAnnotation
- не единственные способы, которые JacksonAnnotationIntrospector
использует для получения или проверки аннотаций После использования аналогичного подхода для создания условных @JsonProperty
аннотация, я заметил, что это не работает для элементов enum. После некоторой отладки я обнаружил, что метод findEnumValues
использует java.lang.reflect.Field::getAnnotation
напрямую (из-за "различных причин", упомянутых в устаревшем findEnumValue
). Если вы хотите, чтобы ваша условная аннотация работала, вы должны переопределить (как минимум) findEnumValues
.
Будьте осторожны с ObjectMapper::setAnnotationIntrospector
Ну, его Javadoc прямо говорит: будьте осторожны. Он заменяет весь интроспектор аннотации вашего картографа, удаляя все добавленные (прикованные) модулями интроспекторов. Он не появился в моем коде в вопросе (это было сделано для создания минимального примера), но на самом деле я случайно сломал десериализацию с KotlinModule
. Вы можете рассмотреть возможность реализации JacksonModule и добавления вашего интроспектора к существующим.
Рассмотрим другой подход: реализация функционально-ориентированных методов в NopAnnotationIntrospector
. В конце я остановился на этом подходе (в основном из-за 4.). Мне нужно было переопределить findEnumValues
и hasIgnoreMarker
, и этого мне было достаточно. Он включал в себя немного кода копирования-вставки из JacksonAnnotationMapper
, но если вам не нужно сделать большую часть аннотации условной, это может сработать (в любом случае, реализация этого требует большого количества стандартного кода). Таким образом, вполне вероятно, что вы действительно хотите связать этот интроспектор, а не set
его.