Tenho um problema no meu desserializador personalizado em Jackson. Desejo acessar o serializador padrão para preencher o objeto para o qual estou desserializando. Depois da população, farei algumas coisas personalizadas, mas primeiro quero desserializar o objeto com o comportamento padrão de Jackson.
Este é o código que tenho no momento.
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;
}
}
O que eu preciso é de uma maneira de inicializar o desserializador padrão para que eu possa pré-preencher meu POJO antes de iniciar minha lógica especial.
Ao chamar desserializar de dentro do desserializador personalizado Parece que o método é chamado a partir do contexto atual, não importa como eu construo a classe do serializador. Por causa da anotação no meu POJO. Isso causa uma exceção Stack Overflow por razões óbvias.
Tentei inicializar um, BeanDeserializer
mas o processo é extremamente complexo e não consegui encontrar a maneira certa de fazer isso. Também tentei sobrecarregar o AnnotationIntrospector
sem sucesso, pensando que isso poderia me ajudar a ignorar a anotação no DeserializerContext
. Por fim, parece que tive algum sucesso com o uso, JsonDeserializerBuilders
embora isso exigisse que eu fizesse algumas coisas mágicas para obter o contexto do aplicativo do Spring. Eu apreciaria qualquer coisa que pudesse me levar a uma solução mais limpa, por exemplo, como posso construir um contexto de desserialização sem ler a JsonDeserializer
anotação.
DeserializationContext
não é algo que você deve criar ou alterar; será fornecido porObjectMapper
.AnnotationIntrospector
, da mesma forma, não ajudará na obtenção de acesso.Respostas:
Como StaxMan já sugeriu, você pode fazer isso escrevendo um
BeanDeserializerModifier
e registrando-o viaSimpleModule
. O seguinte exemplo deve funcionar:fonte
JsonSerializer
? Eu tenho vários serializadores, mas eles têm código em comum, então quero generalizá-lo. Tento chamar diretamente o serializador, mas o resultado não é desembrulhado no resultado JSON (cada chamada do serializador cria um novo objeto)BeanSerializerModifier
,ResolvableSerializer
eContextualSerializer
são as interfaces de correspondência a ser usado para serialização.readTree()
mas a resposta não. Qual é a vantagem dessa abordagem em relação à postada por Derek Cochran ? Existe uma maneira de fazer isso funcionarreadTree()
?Encontrei uma resposta em ans que é muito mais legível do que a resposta aceita.
Realmente não pode ser mais fácil do que isso.
fonte
StackOverflowError
, já que Jackson usará o mesmo serializador novamente paraUser
...O
DeserializationContext
tem umreadValue()
método que você pode usar. Isso deve funcionar para o desserializador padrão e qualquer desserializador personalizado que você tiver.Apenas certifique-se de chamar
traverse()
noJsonNode
nível que deseja ler para recuperar oJsonParser
para passarreadValue()
.fonte
Existem algumas maneiras de fazer isso, mas fazer certo envolve um pouco mais de trabalho. Basicamente, você não pode usar a subclasse, uma vez que as informações de que os desserializadores padrão precisam é criado a partir de definições de classe.
Portanto, o que você provavelmente pode usar é construir um
BeanDeserializerModifier
, registrá-lo viaModule
interface (useSimpleModule
). Você precisa definir / substituirmodifyDeserializer
e, para o caso específico em que deseja adicionar sua própria lógica (em que o tipo corresponde), construir seu próprio desserializador, passar o desserializador padrão que você recebeu. E então, nodeserialize()
método, você pode simplesmente delegar a chamada, pegar o Object resultante.Alternativamente, se você realmente deve criar e preencher o objeto, você pode fazer isso e chamar a versão sobrecarregada
deserialize()
que leva o terceiro argumento; objeto para desserializar.Outra maneira que pode funcionar (mas não 100% certa) seria especificar
Converter
object (@JsonDeserialize(converter=MyConverter.class)
). Este é um novo recurso do Jackson 2.2. No seu caso, o Converter não converteria realmente o tipo, mas simplificaria a modificação do objeto: mas não sei se isso permitiria que você fizesse exatamente o que deseja, já que o desserializador padrão seria chamado primeiro, e só depois o seuConverter
.fonte
BeanDeserializerModifier
é o manipulador de retorno de chamada que permite isso.Se for possível para você declarar uma classe de usuário extra, você pode implementá-la apenas usando anotações
fonte
Seguindo as linhas do que Tomáš Záluský sugeriu , nos casos em que o uso
BeanDeserializerModifier
é indesejável, você pode construir um desserializador padrão usandoBeanDeserializerFactory
, embora haja alguma configuração extra necessária. No contexto, essa solução seria assim:fonte
Aqui está um oneliner usando ObjectMapper
E por favor: realmente não há necessidade de usar nenhum valor String ou qualquer outra coisa. Todas as informações necessárias são fornecidas pelo JsonParser, então use-o.
fonte
Eu não estava ok com o uso,
BeanSerializerModifier
pois força a declarar algumas mudanças comportamentais no central emObjectMapper
vez do próprio desserializador personalizado e, de fato, é uma solução paralela para anotar classes de entidade comJsonSerialize
. Se você acha que é da mesma forma, pode gostar de minha resposta aqui: https://stackoverflow.com/a/43213463/653539fonte
Uma solução mais simples para mim era apenas adicionar outro feijão de
ObjectMapper
e usá-lo para desserializar o objeto (graças ao https://stackoverflow.com/users/1032167/varren comentário) - no meu caso, estava interessado em desserializar para seu id (um int) ou o objeto inteiro https://stackoverflow.com/a/46618193/986160para cada entidade que precisa passar pelo desserializador personalizado, precisamos configurá-la no
ObjectMapper
bean global do Spring Boot App no meu caso (por exemplo, paraCategory
):fonte
Você está fadado ao fracasso se tentar criar seu desserializador personalizado do zero.
Em vez disso, você precisa obter a instância do desserializador padrão (totalmente configurada) por meio de um personalizado
BeanDeserializerModifier
e, em seguida, passar essa instância para sua classe do desserializador personalizado:Nota: O registro deste módulo substitui a
@JsonDeserialize
anotação, ou seja, aUser
classe ou osUser
campos não devem mais ser anotados com esta anotação.O desserializador personalizado deve ser baseado em um
DelegatingDeserializer
para que todos os métodos sejam delegados, a menos que você forneça uma implementação explícita:fonte