As in the header, I'm trying to map 2 classes, but one of the classes has a private property with a private setter.
public Class A {
private String propertyA;
private String propertyB;
ClassA (){
}
public String getPropertyA() {
return propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA= propertyA;
}
public String getPropertyB() {
return propertyB;
}
public void setPropertyB(String propertyB) {
this.propertyB= propertyB;
}
}
public Class B {
private String propertyA;
private String propertyB;
ClassB (String propertyB){
propertyB = propertyB;
}
public String getPropertyA() {
return propertyA;
}
public void setPropertyA(String propertyA) {
this.propertyA= propertyA;
}
public String getPropertyB() {
return propertyB;
}
void setPropertyB(String propertyB) {
this.propertyB= propertyB;
}
}
I want to map objects from class A to class B and the other way around, with the only difference that propertyB does not need to be set in the mapping from class A to B.
I've tried it with the following configuration:
<mapping>
<class-a map-null="false">classA</class-a>
<class-b>classB</class-b>
<field>
<a get-method="getPropertyA" set-method="setPropertyA">propertyA</a>
<b get-method="getPropertyA" set-method="setPropertyA">propertyA</b>
</field>
<field-exclude type="one-way">
<a get-method="getPropertyB" set-method="setPropertyB">propertyB</a>
<b get-method="getPropertyB">propertyB</b>
</field-exclude>
</mapping>
This gives me the exception: Property propertyB for class classB cannot be written to.
This was me intention with the private property, but whatever I do, the exception stays. I've tried to add a field mapping with the type="one-way", but this gives me the same exception.
Is there any way to do this with Dozer?
I think what you are looking for is to add is-accessible="true" to your definition of propertyB for classB. This informs Dozer that it should access the attribute directly, rather than through the getter or setter.
You shouldn't have to specify all the getters and setters if they are following the bean standard. Also, if a field is mapping both ways with the bean standard, you shouldn't have to specify it at all. That means you XML should be able to look something like this:
<mapping>
<class-a map-null="false">classA</class-a>
<class-b>classB</class-b>
<field-exclude type="one-way">
<a>propertyB</a>
<b is-accessible="true">propertyB</b>
</field-exclude>
</mapping>
Related
is it posible to generate a custom "presence checking" method name, being a method of the property itself rather the owning object?
I know I can use hasProperty() methods to check for presence of a value...
https://mapstruct.org/documentation/stable/reference/html/#source-presence-check
but with Optional or JsonNullable (from OpenApi nonullable) that checking method is on the property itself, not on the owning object... :-(
I can map JsonNullable or Optional easyly 'using' or extending a simple custom Mapper
#Mapper
public class JsonNullableMapper {
public <T> T fromJsonNullable(final JsonNullable<T> jsonNullable) {
return jsonNullable.orElse(null);
}
public <T> JsonNullable<T> asJsonNullable(final T nullable) {
return nullable != null ? JsonNullable.of(nullable) : JsonNullable.undefined();
}
}
what I would like to achieve is something like this as "presence check":
if(source.getProperty().isPresent()) {
target.set(customMapper.map(source.getProperty()));
}
Any one found a solution for this?
Thanks and regards
I have managed to implement custom lombok extension which generates "presence checknig" methods.
Here is an example project. In short I added #PresenceChecker annotation and implemented Lombok Javac Annotation handler.
It's possible to use it together with other Lombok annotations:
#Getter
#Setter
public class User {
private String name;
}
#Getter
#Setter
#PresenceChecker
public class UserUpdateDto {
private String name;
}
//MapStruct Mapper interface declaration
#Mapper
public interface UserMapper {
void updateUser(UserUpdateDto dto, #MappingTarget User user);
}
Generated code:
public class User {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
//MapStruct Mapper implementation
public class UserMapperImpl implements UserMapper {
#Override
public void updateUser(UserUpdateDto dto, User user) {
if ( dto == null ) {
return;
}
if ( dto.hasName() ) {
user.setName( dto.getName() );
}
}
}
The answer is unfortunately a straight no.
It is not possible in the current version of MapStruct (1.3.1final) and its not on the shortlist for 1.4.0. You could open up an issue on the git repo of MapStruct as feature request.
I am trying to read value from properties file using #value as follows.
#Value("${abc}")
private String abc;
public List<Record> fetchRecords(String label, String predicate) {
System.out.println(abc);
}
but value of abc is coming as null. Whereas when I try to print the same using #PostConstruct, I am getting the expected value.
#PostConstruct
public void postconstruct() {
System.out.println(abc);
}
Any lead why I am not able to get the value in fetchRecords() method?
For reference, here goes the code
#Component
public class AuditRecord {
private String subject;
private String predicate;
private String oldObject;
private String newObject;
private String readOnlyAuthInfo;
#Value("${registry.system.base}")
private String registrySystemContext;
public void record(DatabaseProvider provider) throws AuditFailedException {
System.out.println("---registrySystemContext value showing null here---"+registrySystemContext);
...
}
#PostConstruct
public void postconstruct() {
System.out.println("---registrySystemContext value showing here as expected---"+registrySystemContext);
}
}
The way I am calling is as follows:
#Component
public class RegistryDaoImpl implements RegistryDao {
...
private void addOrUpdateVertexAndEdge(Vertex v, Vertex dbVertex, GraphTraversalSource dbGraph, String methodOrigin){
...
AuditRecord record = new AuditRecord();
record
.subject(dbVertex.label())
.predicate(e.label())
.oldObject(null)
.newObject(existingV.label())
.record(databaseProvider);
}
}
P.S. registry.system.base is in application.yml.
You need to autowire AuditRecord and not use new directly. Only that way you will have your class in Spring's context.
We don't know your exact usage of the class but you might be interested in Spring's FactoryBean.
I am doing Spring Rest Api project with Spring 4.x
This Works:
Controller.java
#PostMapping("newTransaction")
TransactionRequestModel insertNewTransaction(#RequestBody TransactionRequestModel model){
//do something
}
TransactionRequestModel.java
public class TransactionRequestModel {
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
KeyValue.java
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
Request Body Json
{
"id": 1
"keyValueList": [
{
"key": "dummy",
"value": "dummy"
}
]
}
Spring message converter using jackson is working fine.
This Won't:
When i change TransactionRequestModel.java to following (and delete KeyValue.java)
public class TransactionRequestModel {
public class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
means, making KeyValue an inner class, got following error.
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: No suitable constructor found for type
[simple type, class
com.example.model.TransactionRequestModel$KeyValue]: can not
instantiate from JSON object (missing default constructor or creator,
or perhaps need to add/enable type information?)
Why?
All the related post in SO mentions the first scenario. I would like to know why this wont work. Please help.
You have to make your inner class static.
public class TransactionRequestModel {
public static class KeyValue {
String key;
String value;
//default constructor
//setter-getter
}
private int id;
private List<KeyValue> keyValueList;
public TransactionRequestModel(){}
//default constructor
//getter-setter
}
I get Exception
org.springframework.beans.NotReadablePropertyException: Invalid property 'entries[0].reason' of bean class [my.company.data.SDROrder]: Bean property 'entries[0].reason' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
from the following code snippet:
Errors errors = new BeanPropertyBindingResult(new SDROrder(), "sdr");
orderValidator.validate(order, errors);
for validator:
public class OrderValidator implements Validator
{
#Override
public boolean supports(Class<?> clazz)
{
return Order.class.isAssignableFrom(clazz);
}
#Override
public void validate(final Object target, final Errors errors)
{
errors.rejectValue("entries[0].reason", "Wrong Reason");
}
}
where we have such data hierarchy
public class Order
{
private List<AbstractOrderEntry> entries;
public List<AbstractOrderEntry> getEntries()
{
return entries;
}
public void setEntries(List<AbstractOrderEntry> entries)
{
this.entries = entries;
}
}
public class SDROrder extends Order
{
}
public class AbstractOrderEntry
{
}
public class SDROrderEntry extends AbstractOrderEntry
{
private String reason;
public String getReason()
{
return reason;
}
public void setReason(String reason)
{
this.reason = reason;
}
}
Please see working example here: here
Update 1: Just to clarify. The problem is I try to rejectValue on object that has Collection of objects where each element has specific attribute at Runtime but has not it at Compile time. Spring uses Bean's properties to resolve these fields and can't find inherited attribute. The question is: can I explain Spring to resolve inherited fields somehow?
I found the solution here.
The trick is at
org.springframework.validation.Errors.pushNestedPath(String)
and
org.springframework.validation.Errors.popNestedPath()
methods.
The correct validation should be done as follow:
errors.pushNestedPath("entries[0]");
errors.rejectValue("reason", "Wrong Reason");
errors.popNestedPath();
We are using JPA Entities to get the database rows and then when we transfer that to the external, we want to use disconnected object (DTO) which are simple beans annotated with JAX-B.
We use a mapper and its code looks like this:
public BillDTO map(BillEntity source, BillDTO target) {
BeanUtils.copyProperties(source, target);
return target;
}
But when the code is running we get an error like this:
java.lang.IllegalArgumentException: argument type mismatch
Note this is the Spring implementation of the BeanUtils:
import org.springframework.beans.BeanUtils
And the naming of the properties are identical (with their getter/setter).
Anybody knows why the error happens?
And how to use a fast way instead just copying properties one by one?
This example working well. Here String property is copied to enum property:
Entity:
public class A {
private String valueFrom;
public String getValue() {
return valueFrom;
}
public void setValue(String value) {
this.valueFrom = value;
}
}
DTO (En is enumeration):
public class B {
private En valueTo;
public void setValue(String def) {
this.valueTo = En.valueOf(def);
}
public void setEnumValue(En enumVal) {
this.valueTo = enumVal;
}
}
As for your GitHub example, problem in class B in getter should be:
public String getValue()
Example:
public String getValue() {
return value.toString();
}