杰克逊相当于@XmlSeeAlso [英] Jackson equivalent to @XmlSeeAlso

查看:101
本文介绍了杰克逊相当于@XmlSeeAlso的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing a RESTful web service using Java and Jersey, where the service will accept either XML or JSON inputs. Jackson is used as the JSON deserializer, and integrated into the Jersey config.

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

One of the endpoints is a POST request to a URL, where the content can be one of several different Java classes, and there is a common base class. These classes - with XML annotations - are:

@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

在我的资源中,我可以声明一个方法:

In my resource I can declare a method like:

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

然后我可以POST一个看起来像<$ c $的XML片段c>< firstAction /> ,我的方法将使用 FirstAction 实例调用。到目前为止一直很好。

Then I can POST an XML fragment that looks like <firstAction/> and my method will be invoked with a FirstAction instance. So far so good.

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

Where I'm struggling is getting the JSON deserialization to work as seamlessly as the XML deserialization. Where the @XmlSeeAlso annotation was critical to get the XML deserialization working properly, it seemed that the equivalent for JSON was @JsonSubTypes. So I annotated the classes like this:

// 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} 但是我能得到的是:

I then feed it my test input: { "first-action": null } but all I can get is:

org.codehaus.jackson.map.JsonMappingException:根名称'first-action'与type('action')不匹配[simple type,class com.alu.openstack.domain.compute.server.actions.BaseAction]

"org.codehaus.jackson.map.JsonMappingException: Root name 'first-action' does not match expected ('action') for type [simple type, class com.alu.openstack.domain.compute.server.actions.BaseAction]"

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

Unfortunately since I'm trying to be compatible with someone else's API I can't change my sample input - { "first-action": null } has to work, and deliver to my method an object of class FirstAction. (The action doesn't have any fields, which is why null shouldn't be a problem - it's the type of the class that's important).

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

What's the correct way to have the JSON deserialization work in the same way as the XML deserialization already is?

推荐答案

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

I investigated the use of @JsonTypeInfo but ran into problems because I could not alter the input format. The parser absolutely had to be able to handle input { "first-action":null }. This ruled out the possibility of adding an @type or @class property. Using a wrapper object may have worked, but it choked on the null payload.

关键点是我正在使用UNWRAP_ROOT_PROPERTY配置选项。杰克逊绝对坚持要找一个行动财产,我无法让它考虑其他任何事情。因此,我必须为某些域对象选择性地禁用UNWRAP_ROOT_PROPERTY,以便Jackson可以解析替代方案。我修改了项目的ContextResolver.getContext(...)实现以检查 @JsonRootName 注释 - 因为这只有启用包装的含义,我使用了这个注释,以确定是否返回配置了root属性换行或关闭的对象映射器。

A crucial point was that I was using the UNWRAP_ROOT_PROPERTY configuration option. Jackson was absolutely insisting on finding an action property and I could not get it to consider anything else. So, I had to selectively disable UNWRAP_ROOT_PROPERTY for certain domain objects, so that Jackson would be open to parsing alternatives. I modified the project's ContextResolver.getContext(...) implementation to check for a @JsonRootName annotation - since this only has meaning if wrapping is enabled, I used the presence of this annotation to determine whether to return an object mapper configured with root property wrapping on, or off.

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

At this stage, I might have been able to use @JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...), except for the issue with the null payload mentioned above (this is used to indicate that the child object has no properties - if the spec I was working from had given an empty object {} instead then there would not be a problem). So to proceed I needed a custom type resolver.

我创建了一个扩展 org.codehaus.jackson.map.TypeDeserializer ,目的是每当调用Jackson来反序列化一个 BaseAction 实例时,它都会调用这个自定义反序列化器。反序列化器将被赋予一个子类型数组,用于BaseAction映射第一个动作第二个动作等等。 FirstAction.class等。反序列化器读取字段名称的输入流,然后将名称与类匹配。如果下一个标记是一个对象,那么它会找到并委托给该类的相应反序列化器,或者如果它为null,它会找到no-args构造函数并调用它来获取一个对象。

I created a new class that extended org.codehaus.jackson.map.TypeDeserializer, with the purpose that whenever Jackson is called to deserialize a BaseAction instance, it will call this custom deserializer. The deserializer will be given a subtypes array, which for BaseAction maps first-action, second-action, etc. to FirstAction.class, etc. The deserializer reads the input stream for the field name, then matches the name to a class. If the next token is an object, then it finds and delegates to the appropriate deserializer for that class, or if it is null it finds the no-args constructor and invokes it to get an object.

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

A class that implements org.codehaus.jackson.map.jsontype.TypeResolverBuilder is needed that can build an instance of this previous class, and then the TypeResolverBuilder is given as a @JsonTypeResolver annotation on the BaseAction class.

这篇关于杰克逊相当于@XmlSeeAlso的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆