我正在使用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) {...}
然后我可以发布一个看起来像<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'与type [simple type,class com.alu.openstack.domain.compute.server.actions]的expect('action')不匹配.BaseAction]“
不幸的是,因为我试图与其他人的API兼容,所以无法更改我的示例输入 - { "first-action": null }
必须工作,并向我的方法提供FirstAction类的对象。 (该动作没有任何字段,这就是为什么null不应该是一个问题 - 它是类的重要类型。)
使用JSON反序列化的正确方法是什么,与XML反序列化的方式相同?
答案 0 :(得分:2)
如果您使用的是杰克逊,那么您正在寻找@JsonTypeInfo
和@Type
。有关详细信息,请参阅here
答案 1 :(得分:1)
JSON不像XML那样工作,因此解决方案并不相同。
您需要使用的是(正如其他答案所说),@JsonTypeInfo
。这只会触发包含和使用类型标识符。如果是这样,那么'@JsonSubTypes`将用于反序列化。
必须使用此指标的原因很简单:如果您有多个替代类型要反序列化,则必须要区分。
另请注意,这不一定是属性 - 虽然大多数用户选择“As.PROPERTY”包含,但它不是(IMO)最好的方式。 “WRAPPER_OBJECT”可能就是你要找的东西,因为它增加了一个额外的中间JSON属性,这有点类似于XML。
答案 2 :(得分:0)
我调查了@JsonTypeInfo
的使用但遇到了问题,因为我无法改变输入格式。解析器绝对必须能够处理输入{ "first-action":null }
。这排除了添加@type
或@class
属性的可能性。使用包装器对象可能有效,但它在null
有效负载上被阻塞。
关键是我正在使用UNWRAP_ROOT_PROPERTY配置选项。杰克逊绝对坚持寻找一个action
财产,我无法让它考虑其他任何事情。因此,我必须为某些域对象选择性地禁用UNWRAP_ROOT_PROPERTY,以便Jackson可以解析替代方案。我修改了项目的ContextResolver.getContext(...)实现来检查@JsonRootName
注释 - 因为这只有在启用包装时才有意义,我使用了这个注释的存在来确定是否返回配置的对象映射器root属性包装打开或关闭。
在这个阶段,我可能已经能够使用@JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...)
,除了上面提到的null
有效负载的问题(这用于表示子对象没有属性 - 如果我工作的规范已经给出了一个空对象{}而不是没有问题)。所以要继续我需要一个自定义类型解析器。
我创建了一个扩展org.codehaus.jackson.map.TypeDeserializer
的新类,其目的是每当调用Jackson来反序列化BaseAction
实例时,它都会调用此自定义反序列化器。反序列化器将被赋予一个子类型数组,用于BaseAction将first-action
,second-action
等映射到FirstAction.class等。反序列化器读取字段名称的输入流,然后将名称与一类。如果下一个标记是一个对象,那么它会找到并委托给该类的相应反序列化器,或者如果它为null,它会找到no-args构造函数并调用它来获取一个对象。
需要一个实现org.codehaus.jackson.map.jsontype.TypeResolverBuilder的类,它可以构建这个前一个类的实例,然后在{{1}上给出TypeResolverBuilder作为@JsonTypeResolver
注释} class。