Updating entity using model mapper - java

I have a strange problem with model mapper. Probably I missunderstand how it works.
What I have: an example Model class:
class Model{
String name;
String surname;
Integer age;
...and much much more
And a method
private void foo(){
ModelMapper modelMapper = new ModelMapper();
Model model = Model.builder().name("foo").surname("bar").age(23).build();
Model newModel = Model.builder().name("john").build();
modelMapper.map(newModel, model);
System.out.println(model.toString());
}
And the output is: Model(name=john, surname=null, age=null)
But what I expect Model(name=john, surname=bar, age=23)
Can I do this using model mapper? If not, how to do this easily (i dont want update manually each property)? Thanks.

You can do it easily using Lombok builder #Builder.Default You just need to update your Model class
public class Model {
#Builder.Default
String name = "foo";
#Builder.Default
String surname = "bar";
#Builder.Default
Integer age = 23;

Yes you misunderstood how it works : it works well in your case. It is mapping null values too. If you do not want that, you can use that code inside a Spring Configuration class dedicated to ModelMapper :
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
.setMatchingStrategy(MatchingStrategies.STANDARD)
.setSkipNullEnabled(true);
return modelMapper;
}
The important part is setSkipNullEnabled(true) which handle that globally here. Take care of that when you are handling forms.

Related

How to convert a Properties Object to Custom Object using Jackson?

In a Spring Boot application, Spring Boot is used to build a Properties object from a YAML file as follows:
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(new DefaultResourceLoader().getResource("application.yml"));
Properties properties = yamlFactory.getObject();
The reason why Spring Boot's own parser is used is that it not only reads YAML-compliant settings, but also dot-notated properties like e.g:
artist.elvis.name: "Elvis"
artist.elvis.message: "Aloha from Hawaii"
Now that the Properties object is built, I want to map it into an object like the following for example:
#JsonIgnoreProperties(ignoreUnknown = true)
private record Artist(Elvis elvis) {
private record Elvis(String name, String message) { }
}
My question is:
How can this be done with Jackson? Or is there another/better solution for this?
Many thanks for any help
I saw functionality like that in Ratpack framework.
e.g.:
var propsFileUrl =
Thread.currentThread()
.getContextClassLoader()
.getResource("application.properties");
ApplicationProperties applicationProperties =
ConfigData.builder()
.props(propsFileUrl)
.build()
.get(ApplicationProperties.class);
under the hood it is indeed done by using jackson's object mapper, but the logic is not as trivial to post it here.
here's the library:
https://mvnrepository.com/artifact/io.ratpack/ratpack-core/2.0.0-rc-1
application.yml is the default yml file, so no custom configuration is required. Value annotation should be able to read the properties.
#Value("${artist.elvis.name}")
private String name;
Next part I am not sure about your requirements, but hope this is what you are looking for.
To bind to this object 'constructor' can be a good option.
Class for elvis
#Bean
public class Elvis {
private String name;
private String message;
public Elvis(#Value("${artist.elvis.name"}) final String name, #Value("${artist.elvis.message"}) final String message) {
this.name=name;
this.message=message
}
// getter setter for name and message
}
Now Autowire the created bean to Artist bean
#Bean("artists")
public class Artists {
#Autowired
private Elvis elvis
pubic Elvis getElvis() {
return elvis;
}
}

How to deserialize json data and get rid of all the excess information

I'm having a problem with deserializing json object into DTO class.
The dto object has the following structure:
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#JsonIgnoreProperties(value = { "charsetinfo" })
public class SingleEngineJsonDto {
public List<SingleEngineDTO> engine;
public List<Timetable> timetable;
public List<Dailytable> dailytable;
}
And the output is:
[SingleEngineJsonDto(engine=null, timetable=null, dailytable=null),
SingleEngineJsonDto(engine=[SingleEngineDTO(name=state, … some data)],
timetable=[Timetable(weekDay=1,some data), more data],
dailytable=[Dailytable(date=2018-05-09, more data), more data])]
How do I get rid of this([SingleEngineJsonDto(engine=null, timetable=null, dailytable=null)) part? Original json contains metadata, that I don't need. The firs object in a big json object is ignored metadata and null fields, the second one is null fields filled in. The only idea that I have is list.get(1). And I was told that this solution is wrong.
UPDATE:
Original json structure:
[{"charsetinfo":{"name": "utf-8"}},{"engine":
[{"NAME": "stock","title": some data}],
"timetable":[{"week_day":1,"is_work_day":1,some data},
more data],"dailytable":[{"date": "2018-05-09","is_work_day":0,"start_time": "10:00:00",data}]}]
Desirialization:
#FeignClient(value = "engine", url = "engine")
public interface EngineClient {
#GetMapping
List<EngineJsonDto> getEngines(URI enginesUri,
#RequestParam(value = "lang", required = false) String lang);
#GetMapping
List<SingleEngineJsonDto> getEngine(URI engineUri, #RequestParam(value = "lang", required = false) String lang);
}
Service gets the data from client and gets a list of dto to work with. I use facory pattern to get pieces of data to work with parameters(engine,timetable,dailytable), and all of them had to look the same:
#Service
public class EngineParamEngine implements EngineFactoryInterface {
#Override
public List<SingleEngineDTO> getEngineObjectPart(List<SingleEngineJsonDto> list){
return list.get(1).getEngine(); //<<this
}
}
I was told to change it to list.get(0).Which gives me (engine=null, timetable=null, dailytable=null). So, I need to make the first object disappear somehow. I'm sorry, English is not my first language and I'm new to programming.

ModelMapper - Illegal SourceGetter error when defining a explicit mapping

Part1
I am using the Java ModelMapper library (http://modelmapper.org/) to manage the mappings between my entities and DTOs. I have a Contact (entity) and a ContactView (DTO).
I have a string field in ContactView that doesn't exist in Contact called "type".
Its value should be just the name of the entity's subclass.
I have tried to make this custom mapping like this:
modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> {
mapper.map(src -> src.getClass().getSimpleName(), ContactView::setType);
});
I get a compilation error at:
mapper.map(src -> src.getClass().getSimpleName(), ContactView::setType);
Illegal SourceGetter defined
1 error at
org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
~[spring-beans-5.3.2.jar:5.3.2] at
org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
~[spring-beans-5.3.2.jar:5.3.2] ... 33 common frames omitted
I even tried using a Converter, same result:
modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> {
Converter<Class, String> toName = ctx -> ctx.getSource() == null ? null : ctx.getSource().getSimpleName();
mapper.using(toName).map(Contact::getClass, ContactView::setType);
});
Do you know how to solve this problem?
Part 2
Following up the proposed answer, I tried to add a Converter Class to the ModelMapper. This is where I configure the ModelMapper Bean:
#Configuration
public class Mapper {
#Autowired
private ContactTypeRepository contactTypeRepository;
#Bean
public ModelMapper getMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.typeMap(ContactTag.class, ReferenceEntityView.class).addMappings(mapper -> {
mapper.map(src -> src.getTag().getCode(), ReferenceEntityView::setCode);
mapper.map(src -> src.getTag().getValue(), ReferenceEntityView::setValue);
});
modelMapper.typeMap(Person.class, PersonView.class).addMappings(mapper -> {
mapper.skip(PersonView::setName);
mapper.map(Person::getName, PersonView::setLastName);
});
modelMapper.addConverter(new ContactConverter());
return modelMapper;
}
class ContactConverter implements Converter<Contact, ContactView> {
private ModelMapper localMapper = new ModelMapper();
#Override
public ContactView convert(MappingContext<Contact, ContactView> context) {
Contact contact = context.getSource();
ContactView contactView = localMapper.map(contact, ContactView.class);
ContactType contactType = contactTypeRepository.getByCode(context.getSource().getClass().getSimpleName().toLowerCase());
contactView.setType(localMapper.map(contactType, ReferenceEntityView.class));
return contactView;
}
}
}
This is where I use the ModelMapper Bean to generate my DTO:
#RestController
#RequestMapping(value = "/contacts")
public class ContactController {
#Autowired
private ContactRepository contactRepository;
#Autowired
private ModelMapper modelMapper;
#GetMapping(value = "/{id}")
#ResponseStatus(HttpStatus.OK)
public ContactView findById(#PathVariable("id") Long id){
Contact c = contactRepository.getOne(id);
ContactView cv = modelMapper.map(c, ContactView.class);
return cv;
}
}
For some reason, the convert method from the Converter is not called and the "type" field from the ContactView object is null. The other mappings on the ModelMapper Bean are working properly.
It happens because of the implementation of ModelMapper
public boolean isValid(M member) {
return !Modifier.isStatic(member.getModifiers()) && !member.isSynthetic();
}
And the documentation for isSynthetic method says
Returns true if this member was introduced by the compiler; returns
false otherwise. Returns: true if and only if this member was
introduced by the compiler.
I guess that is why it fails an exception.
For a similar case we introduced a specific mapper class use modelMapper as base mapper and set the other field:
class ContactMapper{
...
public ContactView toView(Contact contact){
ContactView contactView = modelMapper.map(contact,ContactView.class);
contactView.setType(contact.getClass().getSimpleName());
return contactView;
}
To make it consistent with the overall mapping you can define this as a converter and register it to your mapping like this
class ContactConverter implements Converter<Contact, ContactView> {
private final ModelMapper localMapper = new ModelMapper();
#Override
public ContactView convert(MappingContext<Contact, ContactView> context) {
Contact contact = context.getSource();
ContactView contactView = localMapper.map(contact, ContactView.class);
contactView.setType(contact.getClass().getSimpleName());
return contactView;
}
}
ModelMapper modelMapper = new ModelMapper();
modelMapper.addConverter(new ContactConverter());
Try my library beanknife. It's a annotation processor. It means jdk will generate the class for you before you compile the project. So it do the work at compile time. You can use the generated class like any other classes you write yourselives. And you can see the source of the generated class, So no more magic.
This library is able to generate the DTO class for you. You don't need change the original class. In addition to configuring annotations on the original class, you can also choose to create a new configuration class and configure annotations on top of that. The library supports copying and inheriting all properties of the original class, and removing modifying or adding properties base on that. For your question:
// this will generate a DTO class named "ContactView".
// (This is the default name which just append 'View')
// You can change this name using genName attribute.
#ViewOf(value=Contact.class, includePattern = ".*")
public class ContactViewConfigure {
// Add a new property.
// Make sure there is no property named 'type' already in Contact.
// Or you need use #OverrideViewProperty
// There are multi way to add new property.
// In this way, you use a public static method accept the original class instance as the unique argument.
// The new property name is decide by the attribute 'type' of #NewViewProperty.
// So the method name is not important.
#NewViewProperty("type")
public static String type(Contact contact) {
return contact.getClass().getSimpleName()
}
}
// This is the generated class.
public class ContactView {
// other properties
...
private String type;
// getters and setters (By default only getters are generated)
...
// many constructors
...
public static ContactView read(Contact source) {
ContactView out = new ContactView();
// initialize other properties
...
out.type = ContactViewConfigure.type(source);
// initialize other properties
...
return out;
}
// other read method, such as read list, set and map.
...
// other generated methods
...
}
// use like this.
Contact contact = ...
ContactView dto = ContactView.read(contact);
In some situations, beanknife is much powerful than ModelMapper. For example, if something wrong happened, you can check the source of the generated class (Usually located in /target/generated-source/annotations, may differ on IDE), and see why. If it really a bug, you can commit issue to the github, I will deal with it as soon as possible.
Here are more examples.

Jackson json only convert selected fields and methods

With jackson there is a way to ignore some fields using #JsonIgnore. Is there a way to do the opposite, and only show fields with are annotated? I'm working with an external class with a lot of fields and I only want to select a small subset of them. I'm getting tons of recursion problems (using some type of ORM) where object A -> B -> A -> B -> A .... which are not even necessary to export.
You can configure the object mapper to ignore absolutely everything unless specified by JsonProperty,
public class JacksonConfig {
public static ObjectMapper getObjectMapper(){
//The marshaller
ObjectMapper marshaller = new ObjectMapper();
//Make it ignore all fields unless we specify them
marshaller.setVisibility(
new VisibilityChecker.Std(
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE,
JsonAutoDetect.Visibility.NONE
)
);
//Allow empty objects
marshaller.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
return marshaller;
}
}
public class MyObject {
private int id;
#JsonProperty
private String name;
private Date date;
//Getters Setters omitted
in this case only name would be serialized.
Sample repo, https://github.com/DarrenForsythe/jackson-ignore-everything
Yes definitely you can; Create a class with only the feilds you need and add the below property in the object mapper and rest is done.
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES to false
You can use #JsonIgnoreProperties(ignoreUnknown=true) on the pojo class so only the fields which are available in the pojo class will be mapped and resf will be left out.
For example
Json data
{
"name":"Abhishek",
"age":30,
"city":"Banglore",
"state":"Karnatak"
}
pojo class
#JsonIgnoreProperties(ignoreUnknown=true)
Class Person{
private int id;
private String name;
private String city;
}
Here state in not present in the Person class so that field won't be mapped

Spring bean property set using xml configuration from other bean property

I have two bean classes like below
package com.abc;
public class Employee{
private String id;
private String name;
//setters & getters
}
and
package com.cda;
public class EmployeeDTO{
private String id;
private String name;
//setters & getters
}
I want to set the property fields from Employee to EmployeeDTO using spring xml configuration. Where data is coming from some other sources to the Employee Object.
Can you please help me on this scenario.
Use BeanUtils from apache or spring framework instead. Be careful about the argument positioning in these two ways. They are exactly opposite:
org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)
OR
org.springframework.beans.BeanUtils.copyProperties(Object source, Object target)
this is actually not a task that spring does for you. Spring is more about wiring all depending objects together that work together during runtime. What you need is a mapper like mapstruct or enter link description here.
Lets consider a scenario
Where Employee gets it data from a datasource and now you want to map it to EmployeeDto
In such cases:
You can add a constructor in EmployeeDto which takes Employee as parameter and maps the field
You can use ModelMapper where a simple line like:EmployeeDto employeeDto = modelMapper.map(employee, EmployeeDto.class); will work
Use BeanUtils import org.apache.commons.beanutils.BeanUtils;
EmployeeDto newObject = new EmployeeDto();
BeanUtils.copyProperties(newObject, oldObject); reference
Use Jackson ObjectMapper by the convertValue() method: (not recommended due to performance issues)
ObjectMapper mapper = new ObjectMapper();
Employee employee = ...;
EmployeeDto employeeDto = mapper.convertValue(employee, EmployeeDto.class);

Categories

Resources