I have a controller:
@RestController @RequestMapping(value = "/{entity}", produces = MediaType.APPLICATION_JSON_VALUE) public class CrudController<T extends SomeSuperEntity> { @RequestMapping(method = GET) public Iterable<T> findAll(@PathVariable String entity) { } @RequestMapping(value = "{id}", method = GET) public T findOne(@PathVariable String entity, @PathVariable String id) { } @RequestMapping(method = POST) public void save(@PathVariable String entity, @RequestBody T body) { } }
SomeSuperEntity class is as follows:
public abstract class SomeSuperEntity extends AbstractEntity {
And AbstractEntity its abstract class with some field:
public abstract class AbstractEntity implements Comparable<AbstractEntity>, Serializable { private Timestamp firstField; private String secondField; public Timestamp getFirstField() { return firstField; } public void setFirstField(Timestamp firstField) { this.firstField = firstField; } public String getSecondField() { return secondField; } public void setSecondField(String secondField) { this.secondField = secondField; } }
All subclasses of SomeSuperEntity are simple JavaBeans. In the case of the findAll() and findOne(id) methods, everything works fine. I create an object at the service level and return it to the client as JSON with all the fields specified in the subclass and in AbstractEntity .
But when I tried to get the request body in save(entity, body) , I got the following error:
Failed to read the document: it is not possible to instantiate SomeSuperEntity, problem: abstract types either have to be mapped to specific types, have a custom deserializer, or create additional type information
If I remove the abstraction from SomeSuperEntity , everything works, but I request the body, I received only those fields that were declared in AbstractEntity .
And here is my question: are there any ways to solve such problems in my case? If not, what is the best solution here without any structural changes (which makes subcontloller not an option for each object)? Is getting the body plain text would be a good idea? Or would it be better to use Map for this?
I am using Spring v4.2.1 and Jackson 2.6.3 as a converter.
A bit of information about common controllers, but I could not find anything that would cover my case. Therefore, please proceed with a duplication of the question.
Thanks in advance.
UPD : it currently works as follows: I add an extra check to my MessageConverter and define @RequestBody as a String
@Override public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { if (IGenericController.class.isAssignableFrom(contextClass)) { return CharStreams.toString(new InputStreamReader(inputMessage.getBody(), getCharset(inputMessage.getHeaders()))); } return super.read(type, contextClass, inputMessage); }
Then, at the service level, I determine which object I received (in plain json) and converts it:
final EntityMetaData entityMetadata = getEntityMetadataByName(alias); final T parsedEntity = getGlobalGson().fromJson(entity, entityMetadata.getEntityType());
Where EntityMetaData is an enum with certain relationships between the object alias and the class. The alias comes as @PathVariable .