XDR
XDR

Reputation: 4490

Jackson annotation mixin framework

Can the Jackson code that handles annotation mixins be reused by third-parties to mixin non-Jackson annotations?

What are the core Jackson classes that process mixins?

Upvotes: 0

Views: 758

Answers (1)

Meiko Rachimow
Meiko Rachimow

Reputation: 4724

You have to implement the interface com.fasterxml.jackson.databind.ser.ContextualSerializer and/or com.fasterxml.jackson.databind.deser.ContextualDeserializer. Jackson serializes and deserializes JSON. If this is not your intention, you will get some overhead and another approach could be better (e.g. a solution with AspectJ).

If you are interested in the implementation of Jackson and how the (de)serializers are used:

This project contains the general-purpose data-binding functionality and tree-model for Jackson Data Processor. It builds on core streaming parser/generator package, and uses Jackson Annotations for configuration.

A good entry point for analysis: ObjectMapper

This code demonstrates the use of a custom annotation (used in a mixin) and how a suitable (de)serializer could be implemented:

public class MyBean {

    private String value;
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
}

/////

import com.fasterxml.jackson.annotation.JacksonAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation // important so that it will get included!
public @interface MyAnnotation {
    public String value();
}

/////

public abstract class MyMixin {

    @MyAnnotation("Hello")
    public abstract String getValue();
}

/////

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;

import java.io.IOException;

public class MyDeserializer extends JsonDeserializer<String>  implements ContextualDeserializer {

    private String annoValue;

    public MyDeserializer(){}
    public MyDeserializer(MyAnnotation ann) {
        annoValue = ann.value();
    }

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String serializedValue = p.getText();
        return serializedValue.substring(annoValue.length() + 1, serializedValue.length() - 1);
    }

    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty prop) throws JsonMappingException {

        MyAnnotation ann = prop.getAnnotation(MyAnnotation.class);
        if (ann == null) {
            ann = prop.getContextAnnotation(MyAnnotation.class);
        }
        if (ann == null) {
            return new StringDeserializer();
        }
        return new MyDeserializer(ann);
    }

}

/////

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StringSerializer;

import java.io.IOException;

public class MySerializer extends JsonSerializer<String> implements ContextualSerializer {

    private String annoValue;

    public MySerializer() {}
    public MySerializer(MyAnnotation ann) {
        annoValue = ann.value();
    }

    @Override
    public void serialize(String toSerialize, JsonGenerator gen, SerializerProvider prov) throws IOException {
        gen.writeString(annoValue + " " + toSerialize + "!");
    }

    public JsonSerializer createContextual(SerializerProvider prov, BeanProperty prop) throws JsonMappingException {

        MyAnnotation ann = prop.getAnnotation(MyAnnotation.class);
        if (ann == null) {
            ann = prop.getContextAnnotation(MyAnnotation.class);
        }
        if (ann == null) {
            return new StringSerializer();
        }
        return new MySerializer(ann);

    }
}

/////

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(String.class, new MySerializer());
        module.addDeserializer(String.class, new MyDeserializer());
        mapper.registerModule(module);
        mapper.addMixIn(MyBean.class, MyMixin.class);
        MyBean bean = new MyBean();
        bean.setValue("World");
        String jsonInString = mapper.writeValueAsString(bean);
        System.out.println(jsonInString);
        MyBean deserialized = mapper.readValue(jsonInString, MyBean.class);
        System.out.println(deserialized.getValue());
    }
}

Upvotes: 1

Related Questions