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.