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 a JSON deserializer and is integrated into the Jersey configuration.
One endpoint is a POST request for 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 { }
In my resource, I can declare a method like:
@POST @Path("/{id}/action") public Response invokeAction(@PathParam("id") String id, BaseAction action) {...}
Then I can POST an XML fragment that looks like <firstAction/> , and my method will be called by an instance of FirstAction . So far so good.
Where I struggle to make JSON deserialization work as easily as XML deserialization. If the @XmlSeeAlso annotation was critical for XML deserialization to work correctly, it seemed like the equivalent for JSON was @JsonSubTypes . Therefore, I annotated the classes as follows:
// 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
Then I pass it my test input: { "first-action": null } , but all I can get is:
"org.codehaus.jackson.map.JsonMappingException: The root name" first-action "does not match the expected (" action ") for type [simple type, class com.alu.openstack.domain.compute.server.actions.BaseAction]"
Unfortunately, since I'm trying to be compatible with someone else's API, I cannot change my input example - { "first-action": null } should work and deliver an object of class FirstAction to my method. (The action has no fields, so null should not be a problem - this is the type of an important class).
What is the proper way to JSON deserialize to work just like XML deserialization?