I have an abstract class
public abstract class AbstractClazz {
private String discriminator;
// getter, setter
}
and I have multiple implementations of this class, one of them could look like this
public class ConcreteClazz {
private String value;
// getter, setter
}
also I have an endpoint that consumes x-www-form-urlencoded content type only.
@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<Void> test(AbstractClazz abstractClazz) {
System.out.println(abstractClazz);
return ResponseEntity.ok().build();
}
What I want to achieve is to deserialize to a concrete class based on the discriminator parameter in AbstractClazz. I've tried using jackson @JsonSybTypes and @JsonTypeInfo annotations on the abstract class but it only works if the payload comes as json in request body and not as x-www-form-urlencoded content.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "discriminator")
@JsonSubTypes(@JsonSubTypes.Type(value = ConcreteClazz.class, name = "concrete"))
public abstract class AbstractClazz {
private String discriminator;
// getter, setter
}
Is there a way how to achieve the same behavior with x-www-form-urlencoded content type?
The error I get is
Request processing failed: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.AbstractClazz]: Is it an abstract class?] with root cause
java.lang.InstantiationException: null
You can use the RequestParam
annotation that combines query parameters and form data into a single map called "parameters", and that includes automatic parsing of the request body like below:
public ResponseEntity<Void> test(@RequestParam Map<String, String> parameters) {}
Then you can use an ObjectMapper
instance to convert your Map<String, String>
parameters to a subclass of your AbstractClazz
depending from the value of the discriminator key obtaining the expected result:
@PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<Void> test(@RequestParam Map<String, String> parameters) {
ObjectMapper mapper = new ObjectMapper();
AbstractClazz clazz = mapper.convertValue(parameters, AbstractClazz.class);
return ResponseEntity.ok().build();
}