【问题标题】:Jackson equivalent to @XmlSeeAlso杰克逊相当于@XmlSeeAlso
【发布时间】:2012-08-16 10:57:12
【问题描述】:

我正在使用 Java 和 Jersey 编写一个 RESTful Web 服务,该服务将接受 XML 或 JSON 输入。 Jackson 用作 JSON 反序列化器,并集成到 Jersey 配置中。

其中一个端点是对 URL 的 POST 请求,其中的内容可以是几个不同的 Java 类之一,并且有一个公共基类。这些带有 XML 注释的类是:

@XmlRootElement(name = "action")
@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso({ FirstAction.class, SecondAction.class, ThirdAction.class })
public abstract class BaseAction {
}

@XmlRootElement(name = "first-action")
@XmlAccessorType(XmlAccessType.NONE)
public class FirstAction extends BaseAction implements Serializable {
}

// Likewise for SecondAction, ThirdAction

在我的资源中,我可以声明如下方法:

@POST
@Path("/{id}/action")
public Response invokeAction(@PathParam("id") String id, BaseAction action) {...}

然后我可以 POST 一个看起来像 <firstAction/> 的 XML 片段,我的方法将被 FirstAction 实例调用。到目前为止一切顺利。

我正在努力让 JSON 反序列化与 XML 反序列化一样无缝地工作。 @XmlSeeAlso 注释对于使 XML 反序列化正常工作至关重要,而 JSON 的等价物似乎是 @JsonSubTypes。所以我这样注释类:

// XML annotations removed for brevity, but they are present as in the previous code snippet
@JsonSubTypes({ @JsonSubTypes.Type(name = "first-action", value = FirstAction.class),
    @JsonSubTypes.Type(name = "second-action", value = SecondAction.class),
    @JsonSubTypes.Type(name = "third-action", value = ThirdAction.class) })
public abstract class BaseAction {
}

@JsonRootName("first-action")
public class FirstAction extends BaseAction implements Serializable {
}

// Likewise for SecondAction, ThirdAction

然后我将测试输入提供给它:{ "first-action": null },但我能得到的只是:

“org.codehaus.jackson.map.JsonMappingException:根名称'first-action'与类型[简单类型,类com.alu.openstack.domain.compute.server.actions的预期('action')不匹配.BaseAction]"

不幸的是,因为我试图与其他人的 API 兼容,所以我无法更改我的示例输入 - { "first-action": null } 必须工作,并将 FirstAction 类的对象传递给我的方法。 (该操作没有任何字段,这就是为什么 null 不应该成为问题的原因 - 重要的是类的类型)。

让 JSON 反序列化以与 XML 反序列化相同的方式工作的正确方法是什么?

【问题讨论】:

    标签: java json jersey jackson


    【解决方案1】:

    如果您使用的是 Jackson,您正在寻找 @JsonTypeInfo@Type 。更多信息请查看here

    【讨论】:

    • @JsonTypeInfo 似乎是关于用额外的字段来扩充内容以包含类型信息。如问题中所述,我正在编写其他人 API 的兼容实现,因此我无法更改架构,不幸的是,添加字段不是一种选择。除非我遗漏了 @JsonTypeInfo 可以做的事情——在这种情况下你能举个例子吗?
    • 请查看@JsonTypeInfo。这是你需要的。它不需要在 POJO 中添加属性;它只会将它包含在 JSON 中,这是您可以控制的。此外,不要试图让 XML 和 JSON 看起来尽可能相同:JSON 和 XML 数据模型不同(阻抗),因此相同逻辑内容的自然结构自然不同。
    【解决方案2】:

    JSON 不像 XML 那样工作,因此解决方案不完全相同。

    您需要使用的是(就像其他答案所说的那样)@JsonTypeInfo。这只会触发类型标识符的包含和使用。如果是这样,那么 '@JsonSubTypes` 将用于反序列化。

    必须使用此指标的原因很简单:如果您有多个替代类型要反序列化,则必须有一些区别。

    还要注意,这不一定是一个属性——虽然大多数用户选择“As.PROPERTY”包含,但它不是 (IMO) 最好的方法。 “WRAPPER_OBJECT”可能是您要查找的内容,因为它添加了一个额外的中间 JSON 属性,这有点类似于 XML 所做的。

    【讨论】:

      【解决方案3】:

      我调查了@JsonTypeInfo 的使用,但遇到了问题,因为我无法更改输入格式。解析器绝对必须能够处理输入{ "first-action":null }。这排除了添加@type@class 属性的可能性。使用包装器对象可能有效,但它在 null 有效负载上阻塞。

      一个关键点是我使用了 UNWRAP_ROOT_PROPERTY 配置选项。杰克逊绝对坚持要找到action 属性,我无法让它考虑其他任何事情。因此,我必须有选择地为某些域对象禁用 UNWRAP_ROOT_PROPERTY,以便杰克逊对解析替代方案持开放态度。我修改了项目的 ContextResolver.getContext(...) 实现以检查 @JsonRootName 注释 - 因为这仅在启用包装时才有意义,所以我使用此注释的存在来确定是否返回配置有 root 的对象映射器属性环绕打开或关闭。

      在这个阶段,我可能已经能够使用@JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...),除了上面提到的null 有效负载的问题(这用于指示子对象没有属性 - 如果我正在工作的规范from 已经给出了一个空对象 {} ,那么就不会有问题)。所以要继续,我需要一个自定义类型解析器。

      我创建了一个扩展org.codehaus.jackson.map.TypeDeserializer 的新类,目的是每当调用Jackson 来反序列化BaseAction 实例时,它将调用此自定义反序列化器。反序列化器将获得一个子类型数组,用于 BaseAction 将 first-actionsecond-action 等映射到 FirstAction.class 等。反序列化器读取字段名称的输入流,然后将名称与类匹配。如果下一个标记是一个对象,那么它会找到并委托给该类的适当反序列化器,或者如果它是 null,它会找到无参数构造函数并调用它来获取一个对象。

      需要一个实现 org.codehaus.jackson.map.jsontype.TypeResolverBuilder 的类,它可以构建这个前一个类的实例,然后 TypeResolverBuilder 在BaseAction 类上作为@JsonTypeResolver 注释给出。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-07-07
        • 1970-01-01
        • 2011-08-25
        • 2020-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多