2012-08-16 64 views
1

我正在使用Java和Jersey编写一个RESTful Web服务,其中服务将接受XML或JSON输入。 Jackson被用作JSON解串器,并被集成到Jersey配置中。Jackson相当于@XmlSeeAlso

其中一个端点是对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) {...} 

然后我可以发布,看起来像<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:根目录名称“第一-action'与类型[简单类型,类com.alu.openstack.domain.compute.server.actions.BaseAction]不符合预期('action')“

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

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

回答

0

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

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

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

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

实现org.codehaus.jackson.map.jsontype.TypeResolverBuilder的类需要能够建立这个以前类的一个实例,然后将TypeResolverBuilder被给定为一个@JsonTypeResolver注释上BaseAction类。

2

如果您使用的是杰克逊,您正在寻找@JsonTypeInfo@Type。请参阅here以获取更多信息

+0

@ JsonTypeInfo似乎是用增加字段来增加内容以包含类型信息。正如问题中所述,我正在编写其他人的API的兼容实现,所以我无法更改模式,并且不幸的是添加字段不是一个选项。除非我错过@ JsonTypeInfo可以做的事 - 在这种情况下,你可以举个例子吗? – 2012-08-16 12:08:47

+0

请看'@ JsonTypeInfo'。这是你需要的。它不需要在POJO中添加属性;它只会将它包含在JSON中,这是你可以控制的东西。另外,不要试图保持XML和JSON看起来尽可能相同:JSON和XML数据模型是不同的(阻抗),因此相同逻辑内容的自然结构自然不同。 – StaxMan 2012-08-16 17:13:21

1

JSON不支持XML的工作方式,因此解决方案不相同。

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

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

请注意,这不一定是属性 - 而大多数用户选择“As.PROPERTY”包含,它不是(IMO)最好的方式。 “WRAPPER_OBJECT”可能是你正在寻找的东西,因为它增加了一个额外的中间JSON属性,这与XML有些类似。