7

I'm currently working on a project which allows users to book (via the web) the use of a chosen resource for a given period of time. In this program I am trying to keep with Spring's philosophy (and the general best practice) of programming to interfaces and as such I try to use interfaces anywhere where functionality is repeated among concrete classes.

One interface I have created is called a BookableResourceController which specifies the methods needed by a controller to handle the minimum required functionality for any type of resource to be booked. I also make use of a second interface, BookableResource, which identifies which objects model a resource that is allowed to be booked through the application.

The problem I am currently running into is that a few of the methods defined by BookableResourceController use the @RequestBody mapping to convert a JSON object into a method parameter, and since Jackson can only convert JSON into "SimpleType" objects, I receive an error if I specify the input parameter to be a BookableResource.

@RequestMapping(value="/delete.html", method = RequestMethod.POST)  
public ModelAndView processDeleteResource(
    @RequestBody BookableResource resource); 

Can not construct instance of org.codehaus.jackson.map.type.SimpleType, problem: abstract types can only be instantiated with additional type information

From what I can tell this error means that I will need to define a specific implementation of BookableResource, meaning I will most likely need to exclude these methods from the interface even though any controller that is to be used for this purpose will require those methods.

What I am asking is if anyone knows a way to define an interface as the object that is expected from an @RequestBody mapping using JSON, or does anyone have any suggestions of how to structure my contoller interface in order to include these methods?

Cheers

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
Kristen D.
  • 268
  • 9
  • 20

2 Answers2

12

I'm not sure it would work, but you can try to make it generic:

public interface BookableResourceController<R extends BookableResource> {
    @RequestMapping(value="/delete.html", method = RequestMethod.POST)
    public ModelAndView processDeleteResource(@RequestBody R resource); 
}
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • That worked beautifully! I'm not too well versed in generics, so I never would have thought of this. Thank you for the quick response! – Kristen D. Jan 19 '11 at 16:32
2

Additional way to go that can be used is to annotate interface with Jackson annotation:

@JsonDeserialize(as=BookableResourceImp.class)

(possibly using mix-in annotations if one does not want to add it directly in interface class)

EDIT: Another possibility is to use SimpleModule method addAbstractTypeMapping() to specify implementation type. This avoids linkage from interface to implementation, and may (or may not) be more convenient way to register this aspect.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • This solution would couple the interface to the implementation, which seems like a bad practice? – Christopher Z Jun 30 '16 at 03:20
  • 2
    @Chris if you have a choice between bad practice and no solution which one would you choose? Added another way to achieve this. – StaxMan Jul 01 '16 at 19:15
  • I have a case where the interface's default implementation is actually a factory, since I need the data to know which implementation to use, this solution helps a lot to achieve that. – Guillaume F. Oct 18 '21 at 15:02