Reputation: 89
I have a spring-boot app, that has one generic RestController for all api versions and objects, i.e:
@Controller
@Path("{version: v[1-9][0-9]*|latest}")
@RequiredArgsConstructor
public MyController {
private final LoaderFactory loaderFactory;
@PUT
@Path("/{uri: .+}")
public Response update(String requestBody,
@PathParam("version") String versionParam,
@PathParam("uri") String uri) {
Loader loader = loaderFactory.getLoaderForVersion(versionParam);
Introspector introspector = loader.unmarshal(requestBody, Introspector.class);
return writeToDb(introspector);
}
private String writeToDb(Introspector introspector) {...}
}
Loader
and Introspector
are custom classes that use JAXB and Eclipse Persistence under the hood to parse the objects based on a xsd schema (one schema for each api version).
This design is working with defining both the request body and response body as String
and doing the serialization and deserialization in the Controller.
I'd like to change it thus, that it is handled in the background (by Jackson) and the method signature changes to (note the Introspector requestBody
):
@PUT
@Path("/{uri: .+}")
public Response update(Introspector requestBody, ...) {...}
Since the deserialization of Introspector
into an object depends on the api version, the out-of-the-box deserialization of Jackson doesn't work. It somehow has to be instructed to use the correct Loader
instance based on the api version.
So far, I have implemented a custom IntrospectorDeserializer
that implements ContextualDeserializer
which works correctly in a test case:
@Test
public void deserialize() throws IOException, AAIUnmarshallingException {
Loader loader = loaderFactory.getLoaderForVersion("v14");
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Introspector.class, new IntrospectorDeserializer()); // IntrospectorDeserializer
// is my custom class
mapper.registerModule(module);
mapper.setConfig(mapper.getDeserializationConfig().withAttribute("loader", loader));
String customer = new String(Files.readAllBytes(Path.of("src/test/resources/customer.json")));
Introspector introspector = mapper.readValue(customer, Introspector.class);
String result = mapper.writeValueAsString(introspector);
JSONAssert.assertEquals(customer, result, false);
}
Here, the version-specific loader
instance is passed in via mapper.setConfig(mapper.getDeserializationConfig().withAttribute("loader", loader))
.
I don't know if I am heading in the right direction by implementing a custom deserializer.
My question is now, how I would instruct Jackson to somehow make use of my loader class and use the correct loader based on the api version that is provided via the @PathParam
?
Upvotes: 0
Views: 47