I am using www.modelmapper.org and I am working on mapping the same "flat" java DTO into several "hierarchical" DTO.
The "flat" DTO has a number of primitive properties.
The "hierarchical" have a number of complex types that hold a number of primitive types. These DTOs are very similar, but not the same.
"Flat" DTO:
class TransactionRequest
{
String cashierNumber;
Long amount;
Integer currency;
public String getCashierNumber {..
...
}
"Hierarchical DTOs:
class PurchaseRequest
{
POSInfo posInfo;
Amount amount;
...
public PosInfo getPosInfo {..
public Amount getAmount { ..
...
}
class CancelRequest
{
POSInfo posInfo;
Amount amount;
...
public PosInfo getPosInfo {..
public Amount getAmount { ..
...
}
class Amount
{
BigDecimal value;
Integer currency;
public Integer getCurrency{..
...
}
class PosInfo
{
String cashierNumber;
public String getCashierNumber {..
}
TransactionRequest should be mapped into 1) PurchaseRequest and 2) CancelRequest.
One of the problems is, that the amount has to converted from Long (in minor unit) to a BigDecimal (in major unit with decimal digits). I achieved this by writing my own Long to BigDecimal converter. Now I have the problem to define the mappings required in a reusable fashion. What I don't want is to define the mappings for each target type like this:
class PurchaseMap extends PropertyMap<..
protected void configure()
{
using(new LongToBigDecimalConverter(...)).map().getAmount().setValue(source.getAmount());
...
}
class CancelMap extends PropertyMap<..
protected void configure()
{
using(new LongToBigDecimalConverter(...)).map().getAmount().setValue(source.getAmount());
...
}
I would like to have to only one time define the mapping of the Amount DTO (and all other sub type mappings such as PosInfo and many more) and then re-use these mappings. I tried several options:
The first thing I tried was declaring a mapping for TransactionRequest to Amount DTO in my ModelMapper. First, I assumed that simply declaring this mapping would be enough for the mapping mechanism to be also picked up when mapping a TransactionRequest to a PurchaseRequest. That however is not the case.
The second thing I tried does work, but seems to be overly complicated:
I created a PropertyMap for the mapping of TransactionRequest to Amount.
I created a custom converter TransactionRequest to Amount. This converter requires a ModelMapper in its constructor. The ModelMapper I pass to the constructor is the ModelMapper that created the PropertyMap from 1)
I use this converter in the PropertyMaps of PurchaseRequest and CancelRequest
Here is the code:
public class AmountMap extends PropertyMap<TransactionRequest , Amount>
{
....
protected void configure()
{
using(new LongToBigDecimalConverter(...)).map().getAmount().setValue(source.getAmount());
}
...
}
}
public class TransactionRequestToAmountConverter extends AbstractConverter<TransactionRequest, Amount>
{
private final ModelMapper mapper;
public TransactionRequestToAmountConverter(ModelMapper mapper)
{
this.mapper = mapper;
}
public Amount convert(TransactionRequest transactionRequest)
{
return mapper.map(transactionRequest, Amount.class);
}
}
public class PurchaseRequestMap extends PropertyMap<TransactionRequest, PurchaseRequest>
{
private final ModelMapper mapper;
public PurchaseRequestMap(ModelMapper mapper)
{
this.mapper = mapper;
}
protected void configure()
{
using(new TransactionRequestToAmountConverter(mapper)).map(source).setAmount(null);
...
}
}
Is there anybody out there that knows a simpler approach to this?
Try mapping Long to Amount by itself:
modelMapper.createTypeMap(Long.class, Amount.class).setConverter(new Converter<Long, Amount>() {
//... convert Long to Amount
});
That mapping will then be used in your other types and should eliminate the need for the other stuff (if I'm reading your types correctly).
Related
I have ShoppingList service which is responsible for generating shopping list and a IngredientConverter service which is a helping tool for converting objects. My current implementation looks like this
#Service
#AllArgsConstructor
public class ShoppingListService {
private final RecipeService recipeService;
private final IngredientConverter ingredientConverter;
public ShoppingList generateShoppingList(List<UUID> uuidsOfRecipes) {
List<Recipe> recipes = recipeService.getAllByIDIn(uuidsOfRecipes);
ShoppingList shoppingList = ShoppingList.empty();
for (Recipe recipe : recipes) {
shoppingList.addIngredients(recipe.getIngredients());
}
shoppingList.finishAddition(ingredientConverter);
return shoppingList;
}
}
#RequiredArgsConstructor
public class ShoppingList {
#Getter
private final List<IngredientQuantity> optimizedList;
private final Map<Ingredient, Integer> ingredientAmountMap;
public static ShoppingList empty() {
return new ShoppingList(new ArrayList<>(), new HashMap<>());
}
public void addIngredients(List<IngredientQuantity> ingredients) { ... }
public void addIngredient(IngredientQuantity ingredientQuantity) { ... }
public void finishAddition(IngredientConverter ingredientConverter) {
for (Ingredient ingredient : ingredientAmountMap.keySet()) {
IngredientQuantity ingredientQuantity = ingredientConverter.convertWithAmount(
ingredient.getName(),
ingredientAmountMap.get(ingredient),
ingredient.getUnit());
optimizedList.add(ingredientQuantity);
}
}
}
#Service
public class IngredientConverter {
public IngredientQuantity convertWithAmount(String name, int amount, Unit unit) { ... }
}
Is there a better strategy for providing IngredientConverter service to this class? Could I Autowire it somehow despite ShoppingList being POJO class? Should ShoppingList be marked as Component maybe? Not sure what is the best approach.
You cannot autowire service class into POJO. Autowire can be done only within spring managed classes. I can see that ShoppingList is not a spring managed class. Adding #Component will also not be ideal solution. AFAIK, The best solution to use here would be mapStruct. mapStruct can be used to map fields between entity and POJO. And in cases where any field has to be calculated separately, you can write your custom logic and autowire services. Below are steps
Add mapStruct library to pom.xml
Add below mapper class to your project. componentModel="spring" tells the system that this mapper is managed by spring.
All the fields that have same name will be automapped.
For fields which require conversions, you can write #BeforeMapping
Mapper(componentModel="spring")
public abstract class ShoppingListMapper
{
#Autowired
IngredientConverter ingredientConverter; //autowire method you use.
public abstract shoppingListToShoppingListDTO(ShoppingList shoppingList) throws Exception;
public abstract List<ShoppingList> mapShoppingListsToDTOs(List<ShoppingList> shoppingLists) throws Exception;
#BeforeMapping
public void convertLogic(ShoppingList la, #MappingTarget ShoppingListDTO slDto) throws Exception
{
//your logic to set required shoppinglist field using converter
}
}
If this example is not clear, you can refer to web for various mapstruct examples. Let me know if you need further help
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
#Controller
public class Controller{
#RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option1 data1){
return "option1"
}
#RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
You should use JsonNode object.
for your example you should do this:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a #Service or #Repository, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
#GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
#PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// #DeleteMapping, #PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
#Controller
#RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
#Autowired CategoryServ serv;
#Override
public GenericService<Category> getService() {
return serv;
}
}
#Controller
#RequestMapping("/product")
class ProductContr extends GenericController<Product> {
#Autowired ProductServ serv;
#Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
#Service
class CategoryServ extends GenericService<Category> {
#Autowired CategoryRepo repo;
#Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
#Service
class ProductServ extends GenericService<Product> {
#Autowired ProductRepo repo;
#Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
#NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
#Repository
interface CategoryRepo extends GenericRepository<Category> {
}
#Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with #RequestMapping automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added #RequestMapping annotation and then call the super class method.
Another approach is using annotations.
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
#Data
public class Option1And2{
private String name;
private Long id;
}
If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule.
If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
I have the following class and enums:
import lombok.Data;
// other imports...
#Data
public class MapTest{
private MyFirstEnum myFirstEnum;
private MySecondEnum mySecondEnum;
}
public enum MyFirstEnum{
MY_FIRST_ENUM1,
MY_FIRST_ENUM2
}
public enum MySecondEnum {
MY_SECOND_ENUM1,
MY_SECOND_ENUM2
}
and this spring controller:
#PostMapping("/testMap")
#ResponseBody
public void TestMap(#RequestBody MapTest mapTest){
}
Since an enum can be looked up by its name what I would like to do is to post a json to the controller and that the appropriate props will be serialized by their name:
{
"myFirstEnum": "MY_FIRST_ENUM1",
"mySecondEnum": "MY_SECOND_ENUM2"
}
I've tried to set up a #JsonDeserialize but i couldn't get the type of the enum inside the overridden function:
// what type should i use here?
public static class StringToEnum extends JsonDeserializer<???> {
// how do i get the type of the current enum?
#Override
public ??? deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
??? res = Enum.valueOf(p.getText());
return res;
}
}
Update:
I've failed to mention that i'm using lombok's #data attribute for automatically generating getters and setters, which doesn't work well with enum bindings (not sure why).
I guess that laziness comes with a price.
It should be serialized automatically via jackson but you can force it via #JsonCreator
Redefine your enums as
public enum MyFirstEnum{
MY_FIRST_ENUM1,
MY_FIRST_ENUM2;
#JsonCreator
public static MyFirstEnum fromString(String raw) {
return MyFirstEnum.valueOf(raw.toUpperCase());
}
}
Similarly define your second enum as well in the similar manner.
Imp Note (Mandatory)
MapTest should have public setter / getter for both enums (if declared private, preferred), or declare them public (should be avoided, not preferred)
I have a REST API specification that talks with back-end microservices, which return the following values:
On "collections" responses (e.g. GET /users) :
{
users: [
{
... // single user object data
}
],
links: [
{
... // single HATEOAS link object
}
]
}
On "single object" responses (e.g. GET /users/{userUuid}) :
{
user: {
... // {userUuid} user object}
}
}
This approach was chosen so that single responses would be extensible (for example, maybe if GET /users/{userUuid} gets an additional query parameter down the line such at ?detailedView=true we would have additional request information).
Fundamentally, I think it is an OK approach for minimizing breaking changes between API updates. However, translating this model to code is proving very arduous.
Let's say that for single responses, I have the following API model object for a single user:
public class SingleUserResource {
private MicroserviceUserModel user;
public SingleUserResource(MicroserviceUserModel user) {
this.user = user;
}
public String getName() {
return user.getName();
}
// other getters for fields we wish to expose
}
The advantage of this method is that we can expose only the fields from the internally used models for which we have public getters, but not others. Then, for collections responses I would have the following wrapper class:
public class UsersResource extends ResourceSupport {
#JsonProperty("users")
public final List<SingleUserResource> users;
public UsersResource(List<MicroserviceUserModel> users) {
// add each user as a SingleUserResource
}
}
For single object responses, we would have the following:
public class UserResource {
#JsonProperty("user")
public final SingleUserResource user;
public UserResource(SingleUserResource user) {
this.user = user;
}
}
This yields JSON responses which are formatted as per the API specification at the top of this post. The upside of this approach is that we only expose those fields that we want to expose. The heavy downside is that I have a ton of wrapper classes flying around that perform no discernible logical task aside from being read by Jackson to yield a correctly formatted response.
My questions are the following:
How can I possibly generalize this approach? Ideally, I would like to have a single BaseSingularResponse class (and maybe a BaseCollectionsResponse extends ResourceSupport class) that all my models can extend, but seeing how Jackson seems to derive the JSON keys from the object definitions, I would have to user something like Javaassist to add fields to the base response classes at Runtime - a dirty hack that I would like to stay as far away from as humanly possible.
Is there an easier way to accomplish this? Unfortunately, I may have a variable number of top-level JSON objects in the response a year from now, so I cannot use something like Jackson's SerializationConfig.Feature.WRAP_ROOT_VALUE because that wraps everything into a single root-level object (as far as I am aware).
Is there perhaps something like #JsonProperty for class-level (as opposed to just method and field level)?
There are several possibilities.
You can use a java.util.Map:
List<UserResource> userResources = new ArrayList<>();
userResources.add(new UserResource("John"));
userResources.add(new UserResource("Jane"));
userResources.add(new UserResource("Martin"));
Map<String, List<UserResource>> usersMap = new HashMap<String, List<UserResource>>();
usersMap.put("users", userResources);
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(usersMap));
You can use ObjectWriter to wrap the response that you can use like below:
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().withRootName(root);
result = writer.writeValueAsString(object);
Here is a proposition for generalizing this serialization.
A class to handle simple object:
public abstract class BaseSingularResponse {
private String root;
protected BaseSingularResponse(String rootName) {
this.root = rootName;
}
public String serialize() {
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().withRootName(root);
String result = null;
try {
result = writer.writeValueAsString(this);
} catch (JsonProcessingException e) {
result = e.getMessage();
}
return result;
}
}
A class to handle collection:
public abstract class BaseCollectionsResponse<T extends Collection<?>> {
private String root;
private T collection;
protected BaseCollectionsResponse(String rootName, T aCollection) {
this.root = rootName;
this.collection = aCollection;
}
public T getCollection() {
return collection;
}
public String serialize() {
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().withRootName(root);
String result = null;
try {
result = writer.writeValueAsString(collection);
} catch (JsonProcessingException e) {
result = e.getMessage();
}
return result;
}
}
And a sample application:
public class Main {
private static class UsersResource extends BaseCollectionsResponse<ArrayList<UserResource>> {
public UsersResource() {
super("users", new ArrayList<UserResource>());
}
}
private static class UserResource extends BaseSingularResponse {
private String name;
private String id = UUID.randomUUID().toString();
public UserResource(String userName) {
super("user");
this.name = userName;
}
public String getUserName() {
return this.name;
}
public String getUserId() {
return this.id;
}
}
public static void main(String[] args) throws JsonProcessingException {
UsersResource userCollection = new UsersResource();
UserResource user1 = new UserResource("John");
UserResource user2 = new UserResource("Jane");
UserResource user3 = new UserResource("Martin");
System.out.println(user1.serialize());
userCollection.getCollection().add(user1);
userCollection.getCollection().add(user2);
userCollection.getCollection().add(user3);
System.out.println(userCollection.serialize());
}
}
You can also use the Jackson annotation #JsonTypeInfo in a class level
#JsonTypeInfo(include=As.WRAPPER_OBJECT, use=JsonTypeInfo.Id.NAME)
Personally I don't mind the additional Dto classes, you only need to create them once, and there is little to no maintenance cost. And If you need to do MockMVC tests, you will most likely need the classes to deserialize your JSON responses to verify the results.
As you probably know the Spring framework handles the serialization/deserialization of objects in the HttpMessageConverter Layer, so that is the correct place to change how objects are serialized.
If you don't need to deserialize the responses, it is possible to create a generic wrapper, and a custom HttpMessageConverter (and place it before MappingJackson2HttpMessageConverter in the message converter list). Like this:
public class JSONWrapper {
public final String name;
public final Object object;
public JSONWrapper(String name, Object object) {
this.name = name;
this.object = object;
}
}
public class JSONWrapperHttpMessageConverter extends MappingJackson2HttpMessageConverter {
#Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// cast is safe because this is only called when supports return true.
JSONWrapper wrapper = (JSONWrapper) object;
Map<String, Object> map = new HashMap<>();
map.put(wrapper.name, wrapper.object);
super.writeInternal(map, type, outputMessage);
}
#Override
protected boolean supports(Class<?> clazz) {
return clazz.equals(JSONWrapper.class);
}
}
You then need to register the custom HttpMessageConverter in the spring configuration which extends WebMvcConfigurerAdapter by overriding configureMessageConverters(). Be aware that doing this disables the default auto detection of converters, so you will probably have to add the default yourself (check the Spring source code for WebMvcConfigurationSupport#addDefaultHttpMessageConverters() to see defaults. if you extend WebMvcConfigurationSupport instead WebMvcConfigurerAdapter you can call addDefaultHttpMessageConverters directly (Personally I prefere using WebMvcConfigurationSupport over WebMvcConfigurerAdapter if I need to customize anything, but there are some minor implications to doing this, which you can probably read about in other articles.
Jackson doesn't have a lot of support for dynamic/variable JSON structures, so any solution that accomplishes something like this is going to be pretty hacky as you mentioned. As far as I know and from what I've seen, the standard and most common method is using wrapper classes like you are currently. The wrapper classes do add up, but if you get creative with your inheretence you may be able to find some commonalities between classes and thus reduce the amount of wrapper classes. Otherwise you might be looking at writing a custom framework.
I guess you are looking for Custom Jackson Serializer. With simple code implementation same object can be serialized in different structures
some example:
https://stackoverflow.com/a/10835504/814304
http://www.davismol.net/2015/05/18/jackson-create-and-register-a-custom-json-serializer-with-stdserializer-and-simplemodule-classes/
In a Spring Boot/Spring Data Rest project i have issues to use a custom JsonSerializer<Set<Object>> on a #OneToMany property. When i do an HTTP GET /collection request i have the following error:
Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write content: Can not override serializer (through
reference chain:
org.springframework.hateoas.Resources["_embedded"]->java.util.UnmodifiableMap["analogParameters"]->java.util.ArrayList[0]);
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Can not override
serializer (through reference chain:
org.springframework.hateoas.Resources["_embedded"]->java.util.UnmodifiableMap["analogParameters"]->java.util.ArrayList[0])
Below is an extract of my entity class:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="output_parameter_id")
#JsonSerialize(using=InputParametersSerializer.class)
//#Transcient
private Set<InputParameter> inputParameters = new HashSet<InputParameter>();
public Set<InputParameter> getInputParameters() {
return inputParameters;
}
public void setInputParameters(Set<InputParameter> inputParameters) {
this.inputParameters = inputParameters;
}
And the JsonSerializer<Set<InputParameter>>
public class InputParametersSerializer
extends JsonSerializer<Set<InputParameter>> {
static final long serialVersionUID = 123L;
public void serialize (Set<InputParameter> ips, JsonGenerator jg,
SerializerProvider sp)
throws IOException {
jg.writeString("Yeah");
}
}
If i remove #OneToMany and define the property as #transient it works as expected.
InputParameter entity has no Repository associated (it is not exported as a rest resource).
How can a make use of a JsonSerializer on a #OneToMany property?
I ran into a very similar issue while using Spring Boot 2.1.0. Adding a custom serializer, both with using and keyUsing, works fine, but a custom deserializer with a #OneToMany annotated field throws out the same JsonMappingException: Can not override serializer message you got, while with an #ElementCollection it just plain gets ignored. I suspect Spring Data Rest does some undocumented magic in order to take care of the (de)serialization of these kinds of fields that does not play nice with the addition of a custom deserializer. My workaround to this was adding an extra JSON field through an annotated getter and setter. With your example, it would look like:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="output_parameter_id")
private Set<InputParameter> inputParameters = new HashSet<InputParameter>();
public Set<InputParameter> getInputParameters() {
return inputParameters;
}
public void setInputParameters(Set<InputParameter> inputParameters) {
this.inputParameters = inputParameters;
}
#JsonSerialize(using=InputParametersSerializer.class)
public Set<InputParameter> getInputParametersSet() {
return getInputParameters();
}
#JsonDeserialize(using=InputParametersDeserializer.class)
public void setInputParametersSet(Set<InputParameter> inputParameters) {
setInputParameters(inputParameters);
}
Which will output something like
{
...
"inputParameters" : ...,
"inputParametersSet" : ...,
...
}
While not ideal, serialization and deserialization of this field works as expected.
alternatively, in order to keep the field name, a similar workaround worked with #ElementCollection but not with #OneToMany:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="output_parameter_id")
#JsonIgnore
private Set<InputParameter> inputParameters = new HashSet<InputParameter>();
public Set<InputParameter> getInputParameters() {
return inputParameters;
}
public void setInputParameters(Set<InputParameter> inputParameters) {
this.inputParameters = inputParameters;
}
#JsonProperty("inputParameters")
#JsonSerialize(using=InputParametersSerializer.class)
public Set<InputParameter> getInputParametersSet() {
return getInputParameters();
}
#JsonProperty("inputParameters")
#JsonDeserialize(using=InputParametersDeserializer.class)
public void setInputParametersSet(Set<InputParameter> inputParameters) {
setInputParameters(inputParameters);
}
In the end I had to go with the first approach.