Reputation: 2134
I have two different records that I am trying to map using ModelMapper.
Origin record:
package com.myapp.transport.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
public record MetricsDto(
@JsonProperty("category_id") Long categoryId,
@JsonProperty("source") String source,
@JsonProperty("data") MetricsDataDto data
) {
public record MetricsDataDto(
@JsonProperty("c_user_defined_parameters") Double cUserDefinedParameters,
@JsonProperty("c_system_defined_parameters") Double cSystemDefinedParameters,
@JsonProperty("t_user_applied_line_cost") Double tUserAppliedLineCost,
@JsonProperty("t_user_identified_cost") Double tUserIdentifiedCost,
@JsonProperty("t_user_cost") Double tUserCost
) {}
}
Destination record:
package com.myapp.domain.model;
public record Metrics(
Long categoryId,
Source source,
MetricsData data
) {
public record MetricsData(
Double cUserDefinedParameters,
Double cSystemDefinedParameters,
Double tUserAppliedLineCost,
Double tUserIdentifiedCost,
Double tUserCost
) {}
}
Note that the data objects in both records are much larger, about 50 items. Therefore I prefer using records instead of classes to avoid having 50 different getters and setters.
Also, as you can see, the destination record has the source
property as an enum Source
instead of as a string:
package com.myapp.domain.util;
public enum Source {
EXTERNAL() {
@Override
public String toString() {
return "external";
}
},
INTERNAL() {
@Override
public String toString() {
return "internal";
}
},
COMBINED() {
@Override
public String toString() {
return "combined";
}
};
public static Source fromString(String source) throws IllegalArgumentException {
return switch (source) {
case "external" -> EXTERNAL;
case "internal" -> INTERNAL;
case "combined" -> COMBINED;
default -> throw new IllegalArgumentException("Unknown source: " + source);
};
}
}
I am trying to use ModelMapper to map from the origin record to the destination record:
package com.myapp.transport.controller;
import com.myapp.domain.model.Metrics;
import com.myapp.domain.util.Source;
import com.myapp.transport.dto.MetricsDto;
import org.modelmapper.TypeMap;
import org.modelmapper.record.RecordModule;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(Endpoints.METRICS)
public class MetricsController {
private final ModelMapper modelMapper = new ModelMapper().registerModule(new RecordModule());
@PostMapping(value = Endpoints.BULK_ADD, consumes = Constants.APPLICATION_JSON)
public ResponseEntity<Object> bulkAdd(@RequestBody List<MetricsDto> metricsDtos) {
List<Metrics> metrics = metricsDtos
.stream()
.map(metricsDto -> modelMapper.map(metricsDto, Metrics.class))
.toList();
// ... other code that will use the metrics object
}
}
However this does not work:
Failed to instantiate instance of destination com.myapp.domain.model.Metrics. Ensure that com.myapp.domain.model.Metrics has a non-private no-argument constructor.
I assume that there is a problem with
Can someone please help me check what I'm doing wrong.
Upvotes: 0
Views: 80
Reputation: 1980
ModelMapper would not support records by default (or classes with no-args constructor undefined) although in this case you can create a provider for the modelMapper configuration to define how a destination instance should be instantiated. Here's a minimal example based on the ModelMapper Provider docs (https://modelmapper.org/user-manual/providers/):
ModelMapper modelMapper = new ModelMapper();
// Custom provider for Person record
modelMapper.getConfiguration().setProvider(context -> {
Map<String, Object> source = (Map<String, Object>) context.getSource();
return new Person((String) source.get("name"), (Integer) source.get("age"));
});
Map<String, Object> map = Map.of("name", "Alice", "age", 25);
Person person = modelMapper.map(map, Person.class);
System.out.println(person); // Output should be: Person[name=Alice, age=25]
Upvotes: 1