Ciclo de vida de Jackson ObjectMapper en Spring
Ambiente:
Yo uso Spring Boot versión 2.3.3.
Problema
Registro deserializadores personalizados con @JsonComponentanotación.
En algunos módulos y pruebas me @Autowirejacksons ObjectMapper.
Problema 1: No puedo @Autowire ObjectMapper en @JsonComponent o el @JsonComponent se ignorará por completo (sin mensaje de error)
Problema 2: quiero tener la misma instancia de ObjectMapper en @JsonComponent (o al menos OM con la misma configuración) que puedo obtener mediante Autowire en otros componentes
Ejemplo
Como no quiero deserialización personalizada para algunos campos, uso ObjectMapper para usar convertValue()en mis deserializadores personalizados.
Me di cuenta de que no puedo @Autowireusar ObjectMapper en ninguno de mis deserializadores o la configuración no se recogerá para otros ObjectMappers que están conectados automáticamente en mi aplicación (por lo que los deserializadores no están registrados correctamente)
Obtengo ObjectMapper en Deserializer con (ObjectMapper) jsonParser.getCodec();
Pero este ObjectMapper no está configurado correctamente (lo que significa, por ejemplo, que se ignoran las anotaciones en clases / métodos).
Quiero deserializar mi objeto de la siguiente manera:
@Test
void testDeserializeFoo() throws IOException {
ObjectMapper om = new ObjectMapper(); // this is obtained by jsonParser in deserializer but it behaves the same, since the ObjectMapper lacks the configuration
InputStream is = getClass().getResourceAsStream("/json/foo.json");
JsonNode fooNode = om.readTree(is);
Foo foo = om.convertValue(fooNode, Foo.class);
log.info("Foo: " + foo);
}
El constructor de Foo está anotado con @JsonCreator.
El problema es que el ObjectMapper que obtengo del analizador en el Deserializer personalizado ignora esta anotación. Esto da como resultado el siguiente mensaje de error:
Definición de tipo no válida para el tipo
Foo: el argumento n. ° 0 no tiene nombre de propiedad, no es inyectable: no se puede usar como creador [constructor para Foo, anotaciones: {interfaz com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation .JsonCreator (modo = PREDETERMINADO)}]
¿Es posible tener la misma instancia de ObjectMapper en mi Deserializador personalizado que se usa en otros componentes cuando se conecta automáticamente?
Editar
Posible solución / prueba:
Estaba probando mi deserializador de esta manera:
private JsonParser getJsonParser(String resource) throws IOException {
InputStream src = getInputStream(resource);
return objectMapper.createParser(src);
}
@Test
void testDeserializeFoo() throws IOException {
JsonParser parser = getJsonParser("/json/foo.json");
Foo foo = deserializer.deserialize(parser, null);
System.out.println(foo);
}
El problema era que el objectMapper en el método getParser () era una instancia nueva todo el tiempo. Ahora uso el objectMapper autowired. Pero, ¿cómo tiene esto algún efecto?
Conclusión:
Quiero usar la misma instancia de ObjectMapper en mis JsonComponents (deserializadores personalizados) que Autowired ObjectMapper (que se proporciona mediante Spring en otros componentes), o al menos un ObjectMapper con la misma configuración.
Con configuración me refiero a: Funciones de Jackson habilitadas / Configuraciones de anotaciones en clases, métodos, campos / módulos registrados y JsonComponents
¿Alguien puede explicar por favor:
- cómo y cuándo se configura ObjectMapper en Spring (cuándo se registran las configuraciones de anotación (JsonCreator, JsonProperty), las configuraciones de características (como aceptar mayúsculas) en application.yml, JsonComponents personalizados, etc., registrados en un ObjectMapper
- ¿Cuál es el ciclo de vida de esta instancia de ObjectMapper?
- ¿Cuál es el ciclo de vida de una configuración de contexto, configuración de deserialización?
- ¿Cómo se relacionan el contexto de Spring y Jackson entre sí?
Busqué temas relacionados, otras respuestas relacionadas donde:
- El contexto de Spring y Jackson no es el mismo (pero ¿por qué, qué significa esto?)
- Necesita un HandlerInstantiator para Autowire (esto ya es predeterminado en mi versión de primavera, supongo, también esto no explica por qué mis deserializadores personalizados no se recogen sin ningún mensaje de error al conectar automáticamente ObjectMapper en uno de los deserializadores)
Respuestas
En general, puede usar Jackson2ObjectMapperBuilder y build () para generar una nueva instancia de mapeador de objetos con todas las configuraciones recientes aplicadas. El ObjectMapper predeterminado lo proporciona JackonsObjectMapperConfiguration en JackonAutoConfiguration. Todas las instancias del generador de mapeadores de objetos jackson2 son creadas por JacksonObjectMapperBuilderConfiguration en JacksonAutoConfiguration. Los detalles sobre la configuración del mapeador de objetos se pueden encontrar aquí
Puede personalizar aún más el constructor de mapeadores de objetos jackson2 usando Jackson2ObjectMapperBuilderCustomizerConfiguration si no le gustan los valores predeterminados estándar en JacksonAutoConfiguration
Consulte esta respuesta : responderá a sus otras preguntas
Próximamente, tuve el mismo problema al usar jackson2objectbuilder / build o el mapeador de objetos de cableado automático para ser / deser anotado con la anotación JsonComponent. Todas estas anotaciones son escaneadas por el bean JsonComponentModule definido en JacksonAutoConfiguration y están registradas con el mapeador de objetos predeterminado y todas las instancias creadas por el constructor de mapeadores de objetos jackson2. Por lo tanto, no es posible usar el mapeador de objetos no construido y registrar el ser / deser al mismo tiempo. Tiene razón, la excepción (beancurrentlyincreationexception) es suprimida por Spring y el módulo del componente json no está registrado con la referencia circular unresolvale del mensaje para el mapeador de objetos.
No encontré ninguna solución más que crear un nuevo mapeador de objetos fuera de Spring Managed.