Jackson JSON modifies an object before serialization

I want to modify an object right before it is serialized. I want to write my own serializer to parse an object, and then pass it to the default object serializer.

This is what I have:

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); } } 

But this just happens in the infinate loop. I also tried:

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

This works, but does not parse any of the fields in the object.

I also tried using @JsonFilter , but it was extremely heavy and unzipped load times.

Help! Thank!

+5
java json jackson serialization
Aug 26 '14 at 17:18
source share
3 answers

Although I was initially happy at finding @ Nitroware's answer, it unfortunately does not work in Jackson 2.7.2 - BeanSerializerFactory.instance.createSerializer , and then JsonSerializer annotations in the Person class, which leads to infinite recursion and StackOverflowError .

The point at which the default serializer would be created if @JsonSerializer not in the POJO class is the BeanSerializerFactory.constructBeanSerializer method. So let's just use this method directly. Since the method is protected , we make it visible using the factory subclass and pass it information about the serialized class. In addition, we are replacing the legacy SimpleType.construct method with the recommended replacement. Complete solution:

 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); } } 

Unlike the BeanSerializerModifier based BeanSerializerModifier , where you are forced to declare and register special behavior outside your custom serializer, with this solution, the special logic is still encapsulated in the custom PersonSerializer .

Ultimately, the logic can be ported to the user- DefaultJsonSerializerAware ancestor of DefaultJsonSerializerAware .

UPDATE 2017-09-28:

I found the error in the reasoning outlined above. Using a single BeanSerializerFactory.constructBeanSerializer method BeanSerializerFactory.constructBeanSerializer not enough. If the source class contains empty fields, they are not output. (The reason for the constructBeanSerializer method is indirectly called from the createAndCacheUntypedSerializer method, which later calls the addAndResolveNonTypedSerializer method, where the NullSerializer added to BeanPropertyWriter s.))

The solution to this problem, which seems true and fairly simple to me, is to reuse the entire serialization logic, not just the constructBeanSerializer method. This logic starts with the provider serializeValue method. The only inappropriate thing is the JsonSerialize user annotation. Therefore, we redefine BeanSerializationFactory to pretend to be an introspective class (and only it - otherwise JsonSerialize annotations on field types will not be applied) does not have JsonSerialize annotations.

 @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); } 

Please note, if you do not suffer from a problem with zeros, the previous solution is enough for you.

+5
Apr 04 '17 at 17:04 on
source share

Since Jackson 2.2, you can use the converter in the JsonSerialize annotation:

@JsonSerialize(converter = OurConverter.class)

and converter

public class OurConverter extends StdConverter<IN, OUT>

IN and OUT are the same class if you modify the object

+6
Aug 03 '16 at 15:31
source share

Holy crap, after hours of digging in this library, trying to write my own factory and a thousand other things, I FINALLY got this dumb thing to do what I wanted:

 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); } } 
+5
Aug 26 '14 at 19:36
source share



All Articles