使用JSON-B / Yasson将JSON反序列化为多态POJO

问题描述 投票:2回答:1

我在资源类中有一个PATCH端点,而抽象类作为请求主体。我收到以下错误:

22:59:30 SEVERE [or.ec.ya.in.Unmarshaller] (on Line: 64) (executor-thread-63) Can't create instance

似乎因为我要声明为参数的主体模型是抽象的,所以无法反序列化。

我期望得到Element_A或Element_B

如何声明主体是多态的?

这是元素层次结构

public abstract class BaseElement {
    public String name;
    public Timestamp start;
    public Timestamp end;
}

public class Element_A extends BaseElement{
    public String A_data;
}

public class Element_B extends BaseElement{
    public long B_data;
}

这是带有我端点的资源类

@Path("/myEndpoint")
public class ResourceClass {

    @PATCH
    @Path("/{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.TEXT_PLAIN)
    public Response updateEvent(@Valid BaseElement element, @Context UriInfo uriInfo, @PathParam long id) {
        if (element instanceof Element_A) {
            // Element_A logic
        } else if (element instanceof Element_B) {
            // Element_B logic
        }
        return Response.status(Response.Status.OK).entity("working").type(MediaType.TEXT_PLAIN).build();
    }
}

这是我在PATCH请求中发送的请求正文

{
  "name": "test",
  "startTime": "2020-02-05T17:50:55",
  "endTime": "2020-02-05T17:51:55",
  "A_data": "it's my data"
}

我也试图用自定义的反序列化器添加@JsonbTypeDeserializer,但该方法不起作用

@JsonbTypeDeserializer(CustomDeserialize.class)
public abstract class BaseElement {
    public String type;
    public String name;
    public Timestamp start;
    public Timestamp end;
}
public class CustomDeserialize implements JsonbDeserializer<BaseElement> {

    @Override
    public BaseElement deserialize(JsonParser parser, DeserializationContext context, Type rtType) {
        JsonObject jsonObj = parser.getObject();
        String type = jsonObj.getString("type");

        switch (type) {
            case "A":
                return context.deserialize(Element_A.class, parser);
            case "B":
                return context.deserialize(Element_B.class, parser);
        }

        return null;
    }
}

这是我发送的新请求:

{
  "type": "A"
  "name": "test",
  "startTime": "2020-02-05T17:50:55",
  "endTime": "2020-02-05T17:51:55",
  "A_data": "it's my data"
}

引发此错误:

02:33:10 SEVERE [or.ec.ya.in.Unmarshaller] (executor-thread-67) null
02:33:10 SEVERE [or.ec.ya.in.Unmarshaller] (executor-thread-67) Internal error: null

我的pom.xml包括:

<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
java resteasy quarkus jsonb-api yasson
1个回答
0
投票
JSON-B尚未对多态反序列化提供理想的支持,因此,您最好现在可以执行某种解决方法。我们这里有一个有关设计的问题:https://github.com/eclipse-ee4j/jsonb-api/issues/147,所以请给它一个+1来投票!

[使用自定义解串器您有正确的主意,但是问题是您无法解析当前的JsonObject,然后再使用相同的解析器调用context.deserialize(),因为解析器已经超前了读取它的状态。 JSON对象。 (JSONParser是仅转发解析器,因此无法“回退”)

因此,您仍然可以在自定义解串器中调用parser.getObject(),但是一旦确定了具体的JSON类型,就需要使用单独的Jsonb实例来解析该特定JSON字符串。

public static class CustomDeserialize implements JsonbDeserializer<BaseElement> { private static final Jsonb jsonb = JsonbBuilder.create(); @Override public BaseElement deserialize(JsonParser parser, DeserializationContext context, Type rtType) { JsonObject jsonObj = parser.getObject(); String jsonString = jsonObj.toString(); String type = jsonObj.getString("type"); switch (type) { case "A": return jsonb.fromJson(jsonString, Element_A.class); case "B": return jsonb.fromJson(jsonString, Element_B.class); default: throw new JsonbException("Unknown type: " + type); } } }


旁注:您需要将基础对象模型更改为以下内容:

@JsonbTypeDeserializer(CustomDeserialize.class) public abstract static class BaseElement { public String type; public String name; @JsonbProperty("startTime") public LocalDateTime start; @JsonbProperty("endTime") public LocalDateTime end; }

    假设您无法或不想更改JSON模式,则可以更改Java字段名称(startend)以匹配JSON字段名称(startTimeendTime)或者,您可以使用@JsonbProperty批注重新映射它们(我已经在上面完成了)
  1. 由于您的时间戳记采用ISO_LOCAL_DATE_TIME格式,因此我建议使用java.time.LocalDateTime而不是java.sql.Timestamp,因为LocalDateTime处理时区,但最终,无论哪种方式都可以使用您提供的示例数据进行工作
© www.soinside.com 2019 - 2024. All rights reserved.