xmlmapper new deserializar convert java json serialization jackson

java - new - spring boot convert object to xml string



Jackson JSON Modificar objeto antes de la serialización (3)

Estoy buscando modificar un objeto justo antes de que se serialice. Quiero escribir un serializador personalizado para analizar el objeto y luego pasarlo al serializador de objetos predeterminado.

Esto es lo que tengo:

import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; /** * * @author Me */ public class PersonSerializer extends JsonSerializer<Person>{ @Override public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { //This returns a modified clone of Person value. Person safePerson = PrivacyService.getSafePerson(value); provider.defaultSerializeValue(safePerson, jgen); } }

Pero eso solo entra en un ciclo infinate. También he intentado:

provider.findTypedValueSerializer(Person.class, true, null).serialize(safePerson, jgen, provider);

Eso funciona, pero no analiza ninguno de los campos en el objeto.

También traté de usar un @JsonFilter pero era extremadamente pesado y sextuplicaba mis tiempos de carga.

¡Ayuda! ¡Gracias!


Santa mierda, después de varias horas de cavar a través de esta biblioteca, tratando de escribir mi propia fábrica, y un millar de otras cosas, finalmente conseguí esta estúpida cosa para hacer lo que quería:

public class PersonSerializer extends JsonSerializer<Person>{ @Override public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { Person safePerson = PrivacyService.getSafePerson(value); //This is the crazy one-liner that will save someone a very long time BeanSerializerFactory.instance.createSerializer(provider, SimpleType.construct(Person.class)).serialize(safePerson, jgen, provider); } }


Desde Jackson 2.2 uno puede usar el convertidor en la anotación JsonSerialize:

@JsonSerialize(converter = OurConverter.class)

y el convertidor

public class OurConverter extends StdConverter<IN, OUT>

IN y OUT son la misma clase si se modifica el objeto


Aunque inicialmente me regocijé por encontrar la respuesta de @Nitroware, desafortunadamente no funciona en Jackson 2.7.2 - BeanSerializerFactory.instance.createSerializer introspecta la anotación JsonSerializer en la clase Person nuevamente, lo que lleva a la recursión infinita y Error .

El punto donde se crearía el serializador predeterminado si @JsonSerializer no @JsonSerializer en la clase POJO es el método BeanSerializerFactory.constructBeanSerializer . Entonces, usemos este método directamente. Dado que el método está protected , lo hacemos visible a través de la subclase de fábrica y lo alimentamos con información sobre la clase serializada. Además, reemplazamos el método SimpleType.construct desuso por su reemplazo recomendado. La solución completa es:

public class PersonSerializer extends JsonSerializer<PersonSerializer> { static class BeanSerializerFactoryWithVisibleConstructingMethod extends BeanSerializerFactory { BeanSerializerFactoryWithVisibleConstructingMethod() { super(BeanSerializerFactory.instance.getFactoryConfig()); } @Override public JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov, BeanDescription beanDesc) throws JsonMappingException { return super.constructBeanSerializer(prov, beanDesc); } } private final BeanSerializerFactoryWithVisibleConstructingMethod defaultBeanSerializerFactory = new BeanSerializerFactoryWithVisibleConstructingMethod(); private final JavaType javaType = TypeFactory.defaultInstance().constructType(Person.class); @Override public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException { Person safePerson = PrivacyService.getSafePerson(value); JavaType type = TypeFactory.defaultInstance().constructType(Person.class); BeanDescription beanDescription = provider.getConfig().introspect(type); JsonSerializer<Object> defaultSerializer = defaultBeanSerializerFactory.constructBeanSerializer(provider, beanDescription); defaultSerializer.serialize(safePerson, jgen, provider); } }

A diferencia de la solución basada en BeanSerializerModifier , donde se le obliga a declarar y registrar un comportamiento especial fuera de su serializador personalizado, con esta solución, la lógica especial aún se encapsula en PersonSerializer personalizado.

Eventualmente, la lógica podría ser empujada al antecesor DefaultJsonSerializerAware personalizado.

ACTUALIZACIÓN 2017-09-28:

Encontré un error en el razonamiento indicado anteriormente. El uso del método único BeanSerializerFactory.constructBeanSerializer no es suficiente. Si la clase original contiene campos nulos, no están en salida. (La razón es que el método constructBeanSerializer se llama indirectamente desde el método createAndCacheUntypedSerializer que posteriormente llama addAndResolveNonTypedSerializer método NullSerializer donde se agregan BeanPropertyWriter a BeanPropertyWriter s).)

La solución a este problema que me parece correcta y que es bastante simple es reutilizar toda la lógica de serialización, no solo el método constructBeanSerializer . Esta lógica comienza en el método serializeValue del proveedor. Lo único inapropiado es la anotación JsonSerialize personalizada. Por lo tanto, redefinimos BeanSerializationFactory para simular la clase introspected (y solo esto; de lo contrario, las anotaciones de JsonSerialize en los tipos de campo no se aplicarían) no tiene ninguna anotación JsonSerialize .

@Override public void serialize(Person value, JsonGenerator jgen, SerializerProvider provider) throws IOException { Person safePerson = PrivacyService.getSafePerson(value); ObjectMapper objectMapper = (ObjectMapper)jgen.getCodec(); Class<?> entityClass = value.getClass(); JavaType javaType = TypeFactory.defaultInstance().constructType(entityClass); DefaultSerializerProvider.Impl defaultSerializerProvider = (DefaultSerializerProvider.Impl) objectMapper.getSerializerProviderInstance(); BeanSerializerFactory factoryIgnoringCustomSerializerOnRootClass = new BeanSerializerFactory(BeanSerializerFactory.instance.getFactoryConfig()) { @Override protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov, Annotated a) throws JsonMappingException { JsonSerializer<Object> result = javaType.equals(a.getType()) ? null : super.findSerializerFromAnnotation(prov, a); return result; } }; DefaultSerializerProvider.Impl updatedSerializerProvider = defaultSerializerProvider.createInstance(defaultSerializerProvider.getConfig(), factoryIgnoringCustomSerializerOnRootClass); updatedSerializerProvider.serializeValue(jgen, value); }

Tenga en cuenta que si no sufre un problema con valores nulos, la solución anterior es suficiente para usted.