Alejandro Alejandro - 1 month ago 12
Java Question

Spring - Resolve from model to a certain subclass

I have a form for creating an order. An

Order
has a
List
of
Product
s.

The post data may be something like:

client_id=123
product[0].id=1
product[1].id=10
product[2].id=14


The thing is, in my controller, I'm picking up the order with

@ModelAttribute("order") Order newOrder


I guess that's when Spring instantiates the Order object, sets its properties, and then tries to ininitialize the List and add all three Products.

This fails when
Product
is an abstract class, as is my case, and actually have 3 concrete subclasses. So, my question is: at what point and how can I tell Spring to instantiate the Products not directly but resolving to one of its subclasses?

(I can easily have in my form the discriminator value, like so:

product[0].id=1
product[0].type=typeA
product[1].id=10
product[1].type=typeB
product[2].id=14
product[2].type=typeA


)

Answer

I ended up having to implement my own Argument Resolver, as one of the comments pointed out.

public class OrderResolver implements HandlerMethodArgumentResolver {


@Override
public boolean supportsParameter(MethodParameter methodParameter) {
    return methodParameter.getParameterType().equals(Order.class);
}

@Override
public Order resolveArgument(MethodParameter parameter,
                             ModelAndViewContainer mavContainer,
                             NativeWebRequest webRequest,       
                             WebDataBinderFactory binderFactory
                             ) throws Exception {

    Order order = new Order();

    Pattern pattern = Pattern.compile("product\\[(\\d+)\\]\\.type");

    for (String field : webRequest.getParameterMap().keySet()) {
        Matcher matcher = pattern.matcher(field);

        if (matcher.find()) {
            String type = webRequest.getParameter(field);

            Product product = null;

            switch (type) {
                case "typeA": product = new ProductTypeA(); break;
                case "typeB": product = new ProductTypeB(); break;
                case "typeC": product = new ProductTypeC(); break;
                default: continue;
            }

            product.setId(webRequest.getParameter("product[" + matcher.group(1) + "].id"));

            order.getProducts().add(product);
        }
    }       

    return order;

}
Comments