У меня проблема с моим пользовательским десериализатором в Джексоне. Я хочу получить доступ к сериализатору по умолчанию, чтобы заполнить объект, в который я десериализуюсь. После пополнения я сделаю некоторые пользовательские вещи, но сначала я хочу десериализовать объект с поведением Джексона по умолчанию.
Это код, который у меня есть на данный момент.
public class UserEventDeserializer extends StdDeserializer<User> {
private static final long serialVersionUID = 7923585097068641765L;
public UserEventDeserializer() {
super(User.class);
}
@Override
@Transactional
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
User deserializedUser = null;
deserializedUser = super.deserialize(jp, ctxt, new User());
// The previous line generates an exception java.lang.UnsupportedOperationException
// Because there is no implementation of the deserializer.
// I want a way to access the default spring deserializer for my User class.
// How can I do that?
//Special logic
return deserializedUser;
}
}
Мне нужен способ инициализировать десериализатор по умолчанию, чтобы я мог предварительно заполнить свой POJO, прежде чем я начну свою особую логику.
При вызове десериализации из пользовательского десериализатора Кажется, что метод вызывается из текущего контекста независимо от того, как я создаю класс сериализатора. Из-за аннотации в моем POJO. Это вызывает исключение Stack Overflow по очевидным причинам.
Я попытался инициализировать a, BeanDeserializer
но процесс чрезвычайно сложен, и мне не удалось найти правильный способ сделать это. Я также безуспешно пытался перегрузить AnnotationIntrospector
файл, думая, что это может помочь мне игнорировать аннотацию в DeserializerContext
. Наконец, похоже, что я мог бы добиться успеха, JsonDeserializerBuilders
хотя для этого мне потребовалось кое-что волшебное, чтобы получить контекст приложения из Spring. Я был бы признателен за любую вещь, которая могла бы привести меня к более чистому решению, например, как я могу создать контекст десериализации, не читая JsonDeserializer
аннотацию.
DeserializationContext
это не то, что вы должны создавать или изменять; это будет предоставленоObjectMapper
.AnnotationIntrospector
, также не поможет в получении доступа.Ответы:
Как уже предлагал StaxMan, вы можете сделать это, написав
BeanDeserializerModifier
и зарегистрировав его черезSimpleModule
. Следующий пример должен работать:источник
JsonSerializer
? У меня есть несколько сериализаторов, но у них есть общий код, поэтому я хочу его обобщить. Я пытаюсь напрямую вызвать сериализатор, но результат не разворачивается в результате JSON (каждый вызов сериализатора создает новый объект)BeanSerializerModifier
,ResolvableSerializer
иContextualSerializer
являются согласующие интерфейсы , чтобы использовать для сериализации.readTree()
а в ответе нет. В чем преимущество этого подхода по сравнению с тем, что опубликовал Дерек Кокран ? Есть ли способ заставить это работатьreadTree()
?Я нашел ответ на ans, который намного читабельнее, чем принятый ответ.
На самом деле нет ничего проще.
источник
StackOverflowError
, поскольку Джексон снова будет использовать тот же сериализатор дляUser
...У
DeserializationContext
него естьreadValue()
метод, который вы можете использовать. Это должно работать как для десериализатора по умолчанию, так и для любых пользовательских десериализаторов, которые у вас есть.Просто не забудьте вызвать
traverse()
наJsonNode
уровне вы хотите прочитать , чтобы получить ,JsonParser
чтобы перейти кreadValue()
.источник
Есть несколько способов сделать это, но чтобы сделать это правильно, потребуется немного больше работы. По сути, вы не можете использовать подклассы, поскольку информация, необходимая для десериализаторов по умолчанию, строится из определений классов.
Итак, что вы, скорее всего, можете использовать, так это создать
BeanDeserializerModifier
, зарегистрировать его черезModule
интерфейс (использоватьSimpleModule
). Вам нужно определить / переопределитьmodifyDeserializer
, а в конкретном случае, когда вы хотите добавить свою собственную логику (где тип совпадает), создать свой собственный десериализатор, передать десериализатор по умолчанию, который вам предоставлен. А затем вdeserialize()
методе вы можете просто делегировать вызов, взять объект результата.В качестве альтернативы, если вы действительно должны создать и заполнить объект, вы можете сделать это и вызвать перегруженную версию,
deserialize()
которая принимает третий аргумент; объект для десериализации.Другой способ, который может сработать (но не на 100% уверен), - это указать
Converter
object (@JsonDeserialize(converter=MyConverter.class)
). Это новая функция Jackson 2.2. В вашем случае Converter на самом деле не будет преобразовывать тип, а упрощает изменение объекта: но я не знаю, позволит ли это вам делать именно то, что вы хотите, поскольку сначала будет вызываться десериализатор по умолчанию, а только затем вашConverter
.источник
BeanDeserializerModifier
- это обработчик обратного вызова, который позволяет это.Если вы можете объявить дополнительный класс User, вы можете реализовать его, просто используя аннотации
источник
В соответствии с тем, что предложил Томаш Залуски , в случаях, когда использование
BeanDeserializerModifier
нежелательно, вы можете самостоятельно сконструировать десериализатор по умолчаниюBeanDeserializerFactory
, хотя для этого потребуется дополнительная настройка. В контексте это решение могло бы выглядеть так:источник
Вот один лайнер с использованием ObjectMapper
И пожалуйста: действительно нет необходимости использовать какое-либо значение String или что-то еще. Вся необходимая информация предоставляется JsonParser, поэтому используйте ее.
источник
Я был не в порядке с использованием,
BeanSerializerModifier
поскольку он заставляет объявлять некоторые поведенческие изменения в центральном,ObjectMapper
а не в самом настраиваемом десериализаторе, и на самом деле это параллельное решение для аннотирования класса сущности с помощьюJsonSerialize
. Если вы чувствуете это так же, вы можете оценить мой ответ здесь: https://stackoverflow.com/a/43213463/653539источник
Более простым решением для меня было просто добавить еще один bean-компонент
ObjectMapper
и использовать его для десериализации объекта (благодаря комментарию https://stackoverflow.com/users/1032167/varren ) - в моем случае мне было интересно либо десериализовать его по идентификатору (int) или весь объект https://stackoverflow.com/a/46618193/986160для каждого объекта, который должен проходить через настраиваемый десериализатор, нам нужно настроить его в глобальном
ObjectMapper
bean-компоненте приложения Spring Boot в моем случае (например, дляCategory
):источник
Вы обречены на провал, если попытаетесь создать собственный десериализатор с нуля.
Вместо этого вам нужно получить (полностью настроенный) экземпляр десериализатора по умолчанию с помощью настраиваемого
BeanDeserializerModifier
, а затем передать этот экземпляр в свой собственный класс десериализатора:Примечание. Эта регистрация модуля заменяет
@JsonDeserialize
аннотацию, т. Е.User
Класс илиUser
поля больше не должны быть аннотированы этой аннотацией.Пользовательский десериализатор должен быть основан на,
DelegatingDeserializer
чтобы все методы делегировали, если вы не предоставили явную реализацию:источник
Использование
BeanDeserializerModifier
работает хорошо, но если вам нужно использовать,JsonDeserialize
есть способ сделать этоAnnotationIntrospector
следующим образом:Теперь скопированный маппер будет игнорировать ваш пользовательский десериализатор (MyDeserializer.class) и будет использовать реализацию по умолчанию. Вы можете использовать его внутри
deserialize
метода вашего настраиваемого десериализатора, чтобы избежать рекурсии, сделав скопированный сопоставитель статическим или подключив его при использовании Spring.источник