简体   繁体   中英

How to deserialize an object containing a generic type with a builder Jackson?

I'm currently developing a small web application with dropwizard. Another application can send several types of message to my application. These messages have a common base and a specific content depending of the type of message.

Here is the common base of messages:

@JsonDeserialize(builder = BaseMessageBuilder.class)
public abstract class BaseMessage<T> {
    private String commonFiled;
    private T content;

    protected BaseMessage(BaseMessageBuilder<T> builder) {
        this.commonFiled = builder.getCommonField();
        this.content = builder.getContent();
    }

    public String getCommonField() {
        return commonFiled;
    }

    public T getContent() {
        return content;
    }
}

Here is the builder of the common base of messages:

@JsonPOJOBuilder
public class BaseMessageBuilder<T> {
    private String commonFiled;
    private T content;

    public String getCommonField() {
        return commonFiled;
    }

    public BaseMessageBuilder<T> withCommonField(String commonFiled) {
        this.commonFiled = commonFiled;
        return this;
    }

    public String getContent {
        return participants;
    }

    public BaseMessageBuilder<T> withContent(T content) {
        this.content = content;
        return this;
    }

    public BaseMessage<T> build() {
        return new BaseMessage<T>(this);
    }
}

Here is a specific content:

@JsonDeserialize(builder = SpecificContentBuilder.class)
public abstract class SpecificContent{
    private String field1;
    private Long field2;

    protected SpecificContent(SpecificMessageBuilder builder) {
        this.field1 = builder.getField1();
        this.field2 = builder.getField2();
    }

    public String getField1() {
        return field1;
    }

    public Long getField2() {
        return field2;
    }
}

Here is the builder of a specific content:

@JsonPOJOBuilder
public class SpecificContentBuilder {
    private String field1;
    private Long field2;

    public String getField1() {
        return commonFiled;
    }

    public SpecificContentBuilder withField1(String field1) {
        this.field1 = field1;
        return this;
    }

    public String getField2 {
        return field2;
    }

    public SpecificContentBuilder withField2(Long field2) {
        this.field2 = field2;
        return this;
    }

    public BaseMessage build() {
        return new BaseMessage<T>(this);
    }
}

And here is my resource:

@Path("/test")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TestResource {

    @POST
    public void test(BaseMessage<SpecificContent> message) {
        // ...
    }

}

Currently when my resource get the object, the content message don't actually get the right type. When I debug it, it is automatically a LinkedHashMap . I guess there is something wrong. Can anyone help me ?

EDIT :

It is a Jackson issue (Look at pandaadb response). I have deleted my BaseMessageBuilder and I have used a static constructor in order to keep my object immutable.

public abstract class BaseMessage<T> {
    private String commonFiled;
    private T content;

    @JsonCreator
    public static <T> BaseMessage<T> newInstance(
            @JsonProperty("commonFiled") String commonFiled,
            @JsonProperty("content") T content) {
        return new WebhookEventBuilder()
                .withCommonFied(commonFiled)
                .withContent(content)
                .build();
    }

    protected BaseMessage(BaseMessageBuilder<T> builder) {
        this.commonFiled = builder.getCommonField();
        this.content = builder.getContent();
    }

    public String getCommonField() {
        return commonFiled;
    }

    public T getContent() {
        return content;
    }

    private static class BaseMessageBuilder<T> {
        private String commonFiled;
        private T content;

        public WebhookEventType getCommonFiled() {
            return commonFiled;
        }

        public BaseMessageBuilder withCommonFiled(String commonFiled) {
            this.commonFiled = commonFiled;
            return this;
        }

        public T getContent() {
            return content;
        }

        public BaseMessageBuilder withContent(T content) {
            this.content = content;
            return this;
        }

        public BaseMessage build() {
            return new BaseMessage(this);
        }
    }
}

tried this, this is currently not supported it seems:

https://github.com/FasterXML/jackson-databind/issues/921

However, for your example, this is unnecessary. Jackson does the right thing by default, for example, this code example works for me:

I have 1 BaseMessage:

public class BaseMessage<T> {

    @JsonProperty("val1")
    String val1;
    @JsonProperty("val2")
    T val2;
}

And 2 different Content types:

public class Content {

    @JsonProperty("val1")
    String val1;
    @JsonProperty("val2")
    long val2;

}

public class Content2 {

    @JsonProperty("val1")
    String val1;
    @JsonProperty("val2")
    String val2;

}

With this resource:

@Path("/builder")
@Produces(MediaType.APPLICATION_JSON)
public class BuilderResource {


    @POST
    @Path("/test")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(BaseMessage<Content> testContent) {

        System.out.println("hit normal content");
        return Response.ok().build();
    }


    @POST
    @Path("/test2")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response test2(BaseMessage<Content2> testContent) {

        System.out.println("hit String content");
        return Response.ok().build();
    }
}

Hitting the test urls, jackson successfully detects the type T of the base content and creates the correct instance.

The other alternative would be to write your own MessageBodyReader for the BaseMessage type

I hope that helps,

Artur

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM