DavidA
DavidA

Reputation: 4184

How to access default jackson serialization in a custom serializer

I want to create a custom serializer which does a tiny bit of work and then leaves the rest for default serialization.

For example:

@JsonSerialize(using = MyClassSerializer.class)
public class MyClass {
  ...
}

public class MyClassSerializer extends JsonSerializer<MyClass> {
    @Override
    public void serialize(MyClass myClass, JsonGenerator generator, 
                          SerializerProvider provider) 
            throws JsonGenerationException, IOException {
        if (myClass.getSomeProperty() == someCalculationResult) {
            provider.setAttribute("special", true);
        }
        generator.writeObject(myClass);
    }  
}

With the idea of creating other custom serializers for aggregated objects which behave differently based on the 'special' attribute value. However, the above code does not work, as it unsurprisingly goes into an infinite recursion.

Is there a way to tell jackson to use default serialization once I have set the attribute? I don't really want enumerate all the properties like many custom serializers as the class is fairly complex and I don't want to have to do dual maintenance with the serializer every time I change the class.

Upvotes: 33

Views: 29112

Answers (3)

Pawel Zieminski
Pawel Zieminski

Reputation: 534

To add to the chosen answer, the serializer implementation may also have to implement ContextualSerializer and ResolvableSerializer interfaces. Please take a look at a related issue here https://github.com/FasterXML/jackson-dataformat-xml/issues/259

public class MyClassSerializer extends JsonSerializer<MyClass>
    implements ContextualSerializer, ResolvableSerializer {
private final JsonSerializer<Object> defaultSerializer;

public MyClassSerializer(JsonSerializer<Object> defaultSerializer) {
    this.defaultSerializer = checkNotNull(defaultSerializer);
}

@Override
public void serialize(MyClass myclass, JsonGenerator gen, SerializerProvider provider)
        throws IOException {
    if (myclass.getSomeProperty() == true) {
        provider.setAttribute("special", true);
    }
    defaultSerializer.serialize(myclass, gen, provider);
}

@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
        throws JsonMappingException {
    if (defaultSerializer instanceof ContextualSerializer) {
        JsonSerializer<?> contextual = ((ContextualSerializer)defaultSerializer).createContextual(prov, property);
        return new MyClassSerializer((JsonSerializer<Object>)contextual);
    }
    return new MyClassSerializer(defaultSerializer);
}

@Override
public void resolve(SerializerProvider provider) throws JsonMappingException {
    if (defaultSerializer instanceof ResolvableSerializer) {
        ((ResolvableSerializer)defaultSerializer).resolve(provider);
    }
}

}

Also, if you are using Spring Boot, adding a Jackson module is as simple as

@Component
public class MyModule extends SimpleModule {

    @Override
    public void setupModule(SetupContext context) {
        context.addBeanSerializerModifier(new MyBeanSerializerModifier());
        super.setupModule(context);
    }
}

Upvotes: 0

Alex Block
Alex Block

Reputation: 21

You can use @JsonGetter instead of using a custom serializer if that's the only change you want to make.

public class MyClass{

    @JsonGetter("special")
    protected boolean getSpecialForJackson() {
        return myClass.getSomeProperty() == someCalculationResult;
    }

}

Upvotes: 0

Sam Berry
Sam Berry

Reputation: 7844

A BeanSerializerModifier will provide you access to the default serialization.

Inject a default serializer into the custom serializer

public class MyClassSerializer extends JsonSerializer<MyClass> {
    private final JsonSerializer<Object> defaultSerializer;

    public MyClassSerializer(JsonSerializer<Object> defaultSerializer) {
        this.defaultSerializer = checkNotNull(defaultSerializer);
    }

    @Override
    public void serialize(MyClass myclass, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (myclass.getSomeProperty() == true) {
            provider.setAttribute("special", true);
        }
        defaultSerializer.serialize(myclass, gen, provider);
    }
}

Create a BeanSerializerModifier for MyClass

public class MyClassSerializerModifier extends BeanSerializerModifier {
    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        if (beanDesc.getBeanClass() == MySpecificClass.class) {
            return new MyClassSerializer((JsonSerializer<Object>) serializer);
        }
        return serializer;
    }
}

Register the serializer modifier

ObjectMapper om = new ObjectMapper()
        .registerModule(new SimpleModule()
                .setSerializerModifier(new MyClassSerializerModifier()));

Upvotes: 32

Related Questions