首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用JSON-B / Yasson将JSON反序列化为多态POJO

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

Stack Overflow用户
提问于 2020-06-15 23:57:36
回答 1查看 1.5K关注 0票数 5

我在资源类中有一个补丁端点,其中一个抽象类作为请求体。我得到了以下错误:

代码语言:javascript
复制
22:59:30 SEVERE [or.ec.ya.in.Unmarshaller] (on Line: 64) (executor-thread-63) Can't create instance

这似乎是因为我作为一个论点所声明的身体模型是抽象的,所以它不能反序列化。

我希望能得到Element_A或者Element_B

我如何声明身体是多态的?

这是元素层次结构

代码语言:javascript
复制
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;
}

这是带有我的端点的资源类。

代码语言:javascript
复制
@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();
    }
}

这是我在补丁请求中发送的请求体

代码语言:javascript
复制
{
  "name": "test",
  "startTime": "2020-02-05T17:50:55",
  "endTime": "2020-02-05T17:51:55",
  "A_data": "it's my data"
}

我还尝试用自定义反序列化器添加@JsonbTypeDeserializer,但没有工作。

代码语言:javascript
复制
@JsonbTypeDeserializer(CustomDeserialize.class)
public abstract class BaseElement {
    public String type;
    public String name;
    public Timestamp start;
    public Timestamp end;
}
代码语言:javascript
复制
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;
    }
}

这是我发出的新请求:

代码语言:javascript
复制
{
  "type": "A"
  "name": "test",
  "startTime": "2020-02-05T17:50:55",
  "endTime": "2020-02-05T17:51:55",
  "A_data": "it's my data"
}

抛出此错误:

代码语言:javascript
复制
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包括:

代码语言:javascript
复制
<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-06-16 03:57:49

JSON目前还没有对多态反序列化的理想支持,因此目前您最多可以做一种解决方案。我们这里有一个设计问题:https://github.com/eclipse-ee4j/jsonb-api/issues/147,所以请投票给它一个+1

使用自定义反序列化器是正确的,但问题是不能解析当前的JsonObject,然后使用同一个解析器调用context.deserialize(),因为解析器已经超过了读取JSON对象的状态。( JSONParser是一个仅用于前向的解析器,因此没有办法“倒带”它)

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

代码语言:javascript
复制
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);
      }
  }
}

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

代码语言:javascript
复制
  @JsonbTypeDeserializer(CustomDeserialize.class)
  public abstract static class BaseElement {
    public String type;
    public String name;
    @JsonbProperty("startTime")
    public LocalDateTime start;
    @JsonbProperty("endTime")
    public LocalDateTime end;
  }

  1. 假设您不能或不想更改您的JSON模式,您可以更改您的java字段名(startend)以匹配JSON字段名(startTimeendTime),也可以使用@JsonbProperty注释重新映射它们(我在上面已经做过)
  2. ,因为您的时间戳是ISO_LOCAL_DATE_TIME格式的,所以我建议使用java.time.LocalDateTime而不是java.sql.Timestamp,因为LocalDateTime处理时区,但是如果您提供的示例数据H 220G 221G 221,两者最终都能工作。
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62398858

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档