泽西岛定义的 Web 服务是否可以返回未知的子类



我想构建一个Web实用程序模块,该模块使用Jersey 2.0和Jackson 2.2公开一些Web服务。

其中一个 Web 服务必须返回AbstractType的列表。使用实用程序模块的 Web 应用程序定义并将提供一些 ConcreteType 的对象。此具体类型定义如下:

public class ConcreteType extends AbstractType
{
  public int getPropertyDefinedInThisConcreteType()
  {
    return 0;
  }   
}

如果我像这样定义 Web 服务方法:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<? extends AbstractType> getAbstractTypeObjects()
{
  List<? extends AbstractType> concreteTypes = findConcreteTypeFromWebApp();
  return concreteTyypes;
}

泽西岛生成的 JSON 记录仅包含 AbstractType 中的属性,并且不会序列化 propertyDefinedInThisConcreteType 属性。

但是如果我改变上面的方法并写:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<ConcreteType> getAbstractTypeObjects()
{
  List<? extends AbstractType> concreteTypes = findConcreteTypeFromWebApp();
  return concreteTyypes;
}

生成的 JSON 记录现在包括 ConcreteType 类中的属性。

有没有办法在实用程序模块中定义一个 Web 服务,它可以返回使用该模块的任何应用程序中定义和提供的任何具体类型,而无需在编译时知道此具体类型?或者唯一的方法是在知道具体类型的 Web 应用程序中定义 Web 服务?

换句话说,泽西岛多态性是否感知?


尝试澄清我的问题,如果运行以下程序:

public class JacksonTest
{
  static abstract class AbstractType
  {
    public int getPropertyDefinedInAbstractType_1()
    {
      return 1;
    }
    public int getPropertyDefinedInAbstractType_2()
    {
      return 2;
    }
  }
  static class ConcreteType extends AbstractType
  {
    public int getPropertyDefinedInThisConcreteType()
    {
      return 0;
    }
  }
  public static void main(String[] args) throws Exception
  {
    ObjectMapper objectMapper = new ObjectMapper();
    List<? extends AbstractType> abstractTypes = getAbstractTypes();
    String serialize = objectMapper.writeValueAsString(abstractTypes);
    System.out.println(serialize);
  }
  static List<? extends AbstractType> getAbstractTypes()
  {
    List<AbstractType> abstractTypes = new ArrayList<>();
    abstractTypes.add(new ConcreteType());
    abstractTypes.add(new ConcreteType());
    return abstractTypes;
  }
}

我得到以下输出:

[
  {
    "propertyDefinedInThisConcreteType":0,
    "propertyDefinedInAbstractType_1":1,
    "propertyDefinedInAbstractType_2":2
  },
  {
    "propertyDefinedInThisConcreteType":0,
    "propertyDefinedInAbstractType_1":1,
    "propertyDefinedInAbstractType_2":2
  }
]

但是泽西岛在调用此 Web 服务方法时

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<? extends AbstractType> getAbstractTypeObjects()
{
  List<? extends AbstractType> abstractTypes = getAbstractTypes();
  return abstractTypes;
}

生产:

[
  {
    "propertyDefinedInAbstractType_1":1,
    "propertyDefinedInAbstractType_2":2
  },
  {
    "propertyDefinedInAbstractType_1":1,
    "propertyDefinedInAbstractType_2":2
  }
]

你需要使用杰克逊多态性,例如查看注释@JsonTypeInfo和@JsonTypeName/@JsonSubTypes

http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

这是一种奇怪的行为,我无法解释,但在玩弄它时,我能够重现问题。奇怪的是,为了纠正这种行为,我不得不返回Response而不是List<? extends AbstractType>

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAbstractTypeObjects() {
    List<? extends AbstractType> concreteTypes = getAbstractTypes();
    return Response.ok(concreteTypes).build();
}
static List<? extends AbstractType> getAbstractTypes() {
    List<AbstractType> abstractTypes = new ArrayList<>();
    abstractTypes.add(new ConcreteType());
    abstractTypes.add(new ConcreteType());
    return abstractTypes;
}


C:>curl -v http://localhost:8080/api/types [ { "propertyDefinedInThisConcreteType" : 0, "propertyDefinedInAbstractType_1" : 1, "propertyDefinedInAbstractType_2" : 2 }, { "propertyDefinedInThisConcreteType" : 0, "propertyDefinedInAbstractType_1" : 1, "propertyDefinedInAbstractType_2" : 2 } ]

它工作正常。就像我说的,我真的无法解释这个beahavior的原因:-(

我的猜测是返回Response会导致发现属性,而返回AbstractType只会导致使用AbstractType的属性。

在阅读了上面的两个答案并尝试了一段时间后,我认为我可以发布我问题的答案。

我注意到,如果我将 Web 服务方法定义为:

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<? extends Object> getAbstractTypeObjects()
{
  ...
}

还会输出完整的 ConcreteType 对象。

似乎 Jersey 向 Web 服务客户端保证,它们将收到的属性将与声明的返回类型兼容。但是,如果将Object声明为返回类型,则所有属性都包含在 JSON 寄存器中。我不知道这一切是否都在规范中。

如果在

AbstractType 类中使用 @JsonTypeInfo,则始终独立于 Web 服务中声明的返回类型发送ConcreteType对象的所有属性。该方法的问题在于您需要有权访问 ConcreteType 类,因为类型信息也在 JSON 输出中编码。如果不使用 @JsonTypeInfo则可以在客户端中声明另一个类,该类具有与用于接收 Web 服务输出的实际发送的类相同的属性。

如果尝试在 AbstractType 类的声明中使用 @JsonTypeInfo(use=Id.NONE) 以避免这最后的不便,则再次不会输出 ConcreteType 属性。

最新更新