I have a requirement to add firstName and lastName from source to fullName in target. I want first & last name to be separated by space. But I am unable to write the proper mapping for it.
My Source and target class-
public class Source {
private int id;
private String firstName;
private String lastName;
private List<String> addressList;
}
public class Target {
private int id;
private String fullName;
private String city;
}
Here is my mapper interface-
#Mapper
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
#Mapping(expression = "java(source.getFirstName()+source.getLastName())", target = "fullName")
#Mapping(expression = "java(source.getAddressList().get(0))", target = "city")
Target sourceToTarget(Source source);
#InheritInverseConfiguration
Source targetToSource(Target target);
}
Adding space in middle is giving me error-
#Mapping(expression = "java(source.getFirstName()+" "+source.getLastName())", target = "fullName")
If there is any other solution or any approach for this ?
You will need to escape the quote ".
e.g.
#Mapping(expression = "java(source.getFirstName()+ \" \" +source.getLastName())", target = "fullName")
I found another way to write the Mapping by using decorator pattern as mentioned in the Map Struct documentation by using the decorator pattern.
I will just copy paste the link and example here.
https://mapstruct.org/documentation/dev/reference/html/#customizing-mappers-using-decorators
And here goes the code taken from mapstruct documentation -
#Mapper
#DecoratedWith(PersonMapperDecorator.class)
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
PersonDto personToPersonDto(Person person);
AddressDto addressToAddressDto(Address address);
}
public abstract class PersonMapperDecorator implements PersonMapper {
private final PersonMapper delegate;
public PersonMapperDecorator(PersonMapper delegate) {
this.delegate = delegate;
}
#Override
public PersonDto personToPersonDto(Person person) {
PersonDto dto = delegate.personToPersonDto( person );
dto.setFullName( person.getFirstName() + " " + person.getLastName() );
return dto;
}
}
Related
inside my test class
String json="{\n" +
"\t\"masterName\": \"test1\",\n" +
"\t\"masterSubjectName\": \"testsubject\",\n" +
"\t\"masterRoll\": \"534\",\n" +
"\t\"firstName\": \"studentFirstName\",\n" +
"\t\"rollNumber\": \"23\"\n" +
"}";
Student studentDTO=new Gson().fromJson(json, Student.class);
System.out.println(studentDTO);
Student.java
#Data
public class Student {
#Delegate(types = Master.class)
private Master master=new Master();
private String firstName;
private String lastName;
private int rollNumber;
}
Master.java
#Data
public class Master {
private String masterName;
private String masterSubjectName;
private int masterRoll;
}
This gives Response:
Student(master=Master(masterName=null, masterSubjectName=null, masterRoll=0), firstName=studentFirstName, lastName=null, rollNumber=23)
When I parse the json string "json" to the Student class,
Why values not getting passed to "Master.java" inner dto?
I need something like Student(masterName=test1, masterSubjectName=testsubject, masterRoll=534, firstName=studentFirstName,rollNumber=23)
Your 'types' value in #Delegate should be Interface with getter methods for example getMasterName() , getMasterSubjectName... and so on
documentation:
https://projectlombok.org/features/experimental/Delegate
I'm using Jackson mixins to only serialize out specific fields.
My ObjectMapper is configured like so:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.addMixIn(Person.class, SyncPerson.class);
mapper.addMixIn(TransactionLog.class, TransactionLogExport.class);
Here are the model classes paired with the JSON mixin objects that I'd like to export:
// Model class
public class Person {
private Long id;
private String email;
private String firstName;
private String lastName;
}
// Desired JSON format. Excludes 'id' field
public interface SyncPerson {
#JsonProperty("firstName")
String getFirstName();
#JsonProperty("lastName")
String getLastName();
#JsonProperty("email")
String getEmail();
}
// Model class
public class TransactionLog {
private long id;
private Integer version;
private Person person;
private Date date;
private EntityAction action;
}
// Desired JSON format. Excludes 'id' field, 'version', 'date'
public interface TransactionLogExport {
#JsonProperty("id")
String getId();
#JsonProperty("person")
Person person();
#JsonProperty("action")
EntityAction getAction();
}
Yet, my tests are showing that the person attribute of the TransactionLog isn't coming through.
#Test
public void testWriteValue() throws Exception {
Person person = new Person();
person.setEmail("a#c.com");
person.setFirstName("A");
person.setLastName("C");
TransactionLog log = new TransactionLog();
log.setId(0L);
log.setAction(EntityAction.CREATE);
log.setPerson(person);
log.setStartValue("start");
log.setEndValue("end");
log.setChanges("change");
String prettyJson = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(log);
System.out.println(prettyJson);
// Prints:
// {
// "id" : 0,
// "action" : "CREATE",
}
}
If I try the same test with a regular ObjectMapper mapper = new ObjectMapper(); instead of the mixin, then I see the full object exported, including the Person with email, names, etc. So something must be wrong with how I've configured the mixin... or else I'm misunderstanding something.
So can anyone help indicate what I could do to export out the subtype 'person' in my mixin?
Thanks!
Finally figured out the issue. The test now prints what we want:
{
“id” : 0,
“person” : {
“email” : “a#c.com”,
“firstName” : “A”,
“lastName” : “C”
},
“action” : “CREATE”
}
The mistake was in TransactionLogExport. It needs to say:
#JsonProperty("person")
Person getPerson();
Instead of:
#JsonProperty("person")
Person person();
I.e. the method needs to start with 'get'.
I have a list of string in my DTO, i want to map it into a list of object, in the mapper i used the service to get the object by this string, but i have the below error
Can't map property "java.util.List<java.lang.String> customers" to
"java.util.List<com.softilys.soyouz.domain.Customer> customers".
Consider to declare/implement a mapping method:
"java.util.List<com.softilys.soyouz.domain.Customer>
map(java.util.List<java.lang.String> value)".
public class FirstDomain implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
private String description;
private List<Customer> customers;
}
public class FirstDomainDTO {
private String id;
private String description;
private List<String> customers;
}
#Mapper(uses = { CustomerService.class })
public interface FirstDomainMapper extends EntityMapper<FirstDomainDTO, FirstDomain> {
#Mapping(source = "customers", target = "customers")
FirstDomainDTO toDto(FirstDomain firstDomain);
#Mapping(source = "customers", target = "customers")
FirstDomain toEntity(FirstDomainDTO firstDomainDTO);
default String fromCustomer(Customer customer) {
return customer == null ? null : customer.getCode();
}
}
The error message you are getting should be enough to help you understand what the problem is. In this case MapStruct doesn't know how to map from List<String> into List<Customer>. The other way around is OK since you have defined
default String fromCustomer(Customer customer) {
return customer == null ? null : customer.getCode();
}
To fix this you need to defined the reverse as well.
#Mapper(uses = { CustomerService.class })
public interface FirstDomainMapper extends EntityMapper<FirstDomainDTO, FirstDomain> {
#Mapping(source = "customers", target = "customers")
FirstDomainDTO toDto(FirstDomain firstDomain);
#Mapping(source = "customers", target = "customers")
FirstDomain toEntity(FirstDomainDTO firstDomainDTO);
default String fromCustomer(Customer customer) {
return customer == null ? null : customer.getCode();
}
default Customer fromStringToCustomer(String customerId) {
// Implement your custom mapping logic here
}
}
I want to map the Abc class to AbcDTO using "org.mapstruct.Mapping"
class Abc {
private List<Xyz> xyz = null;
private String uvw;
private String cde;
}
class AbcDTO{
private List<XyzDTO> xyz = null;
private String uvw;
private String cde;
}
class Xyz{
private String type;
private String value;
private String docId;
}
class XyzDTO{
private String type;
private String value;
private DocDTO document;
}
I tried to map the classes by using the annotation:
#Mappings({
#Mapping(source = "xyz.docId", target = "xyz.doc")
})
abcDTO abcToabcDTO(abc abc)
Can someone please help with how do i iterate through the nested beans and map the docId to doc?
If the names are same they map automatically but I want to map from docId to doc.
when you want map list you can define it :
#Mapping(source="docId", target="doc")
XyzDTO xyzToXyzDTO(XyZ xyz);
#Mapping(source="xyz", target="xyz") //useless if two lists got same name, but good for comprehention
AbcDTO abcToAbcDTA(Abc abc);
It should be as below (
you can give it a try):
#Mappings({
#Mapping(target="doc", source="abc.docId")
})
AbcDTO abcToabcDTO(Abc abc);```
I have a JSON payload that looks like this:
{
"id": 32,
"name": "[Sample] Tomorrow is today, Red printed scarf",
"primary_image": {
"id": 247,
"zoom_url": "www.site.com/in_123__14581.1393831046.1280.1280.jpg",
"thumbnail_url": "www.site.com/in_123__14581.1393831046.220.290.jpg",
"standard_url": "www.site.com/in_123__14581.1393831046.386.513.jpg",
"tiny_url": "www.site.com/in_123__14581.1393831046.44.58.jpg"
}
}
Can I unwrap a specific field and discard all the others? In other words, can I bind this directly to a POJO like this:
public class Product {
private Integer id;
private String name;
private String standardUrl;
}
There are lots of ways. Do you need to deserialize, serialize or both?
One way to deserialize would be to use a creator method that takes the image as a tree node:
public static class Product {
private Integer id;
private String name;
private String standardUrl;
public Product(#JsonProperty("id") Integer id,
#JsonProperty("name") String name,
#JsonProperty("primary_image") JsonNode primaryImage) {
this.id = id;
this.name = name;
this.standardUrl = primaryImage.path("standard_url").asText();
}
}
The creator doesn't have to be a constructor, you could have a static method that is only used for Jackson deserialization.
You'd have to define a custom serializer to reserialize this, though (e.g. a StdDelegatingSerializer and a converter to wrap the string back up as an ObjectNode)
There are different ways to skin this cat, I hope you can use Jackson 2 for this, since it offers great ways to deserialize Json data, one of my favorites deserialization features is the one I'll show you here (using Builder Pattern) because allows you to validate instances when they are being constructed (or make them immutable!). For you this would look like this:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.Map;
#JsonDeserialize(builder = Product.Builder.class)
public class Product {
private Integer id;
private String name;
private String standardUrl;
private Product(Builder builder) {
//Here you can make validations for your new instance.
this.id = builder.id;
this.name = builder.name;
//Here you have access to the primaryImage map in case you want to add new properties later.
this.standardUrl = builder.primaryImage.get("standard_url");
}
#Override
public String toString() {
return String.format("id [%d], name [%s], standardUrl [%s].", id, name, standardUrl);
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Builder {
private Integer id;
private String name;
private Map<String, String> primaryImage;
public Builder withId(Integer id) {
this.id = id;
return this;
}
public Builder withName(String name) {
this.name = name;
return this;
}
#JsonProperty("primary_image")
public Builder withPrimaryImage(Map<String, String> primaryImage) {
this.primaryImage = primaryImage;
return this;
}
public Product build() {
return new Product(this);
}
}
}
To test it I created this class:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Test {
public static void main(String[] args) {
String serialized = "{" +
" \"id\": 32," +
" \"name\": \"[Sample] Tomorrow is today, Red printed scarf\"," +
" \"primary_image\": {" +
" \"id\": 247," +
" \"zoom_url\": \"www.site.com/in_123__14581.1393831046.1280.1280.jpg\"," +
" \"thumbnail_url\": \"www.site.com/in_123__14581.1393831046.220.290.jpg\"," +
" \"standard_url\": \"www.site.com/in_123__14581.1393831046.386.513.jpg\"," +
" \"tiny_url\": \"www.site.com/in_123__14581.1393831046.44.58.jpg\"" +
" }" +
" }";
ObjectMapper objectMapper = new ObjectMapper();
try {
Product deserialized = objectMapper.readValue(serialized, Product.class);
System.out.print(deserialized.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
The output is (using the override toString() method in Product:
id [32], name [[Sample] Tomorrow is today, Red printed scarf], standardUrl [www.site.com/in_123__14581.1393831046.386.513.jpg].
There are two ways to get the response you required. For both methods, we are going to use JsonView.
Create two types of JsonView:
public interface JViews {
public static class Public { }
public static class Product extends Public { }
}
First method
#JsonView(JViews.Public.class)
public class Product {
private Integer id;
private String name;
#JsonIgnore
private Image primaryImage;
#JsonView(JViews.Product.class)
public String getStandardUrl{
return this.primaryImage.getStandardUrl();
}
}
Second way
Using Jackson's #JsonView and #JsonUnwrapped together.
#JsonView(JViews.Public.class)
public class Product {
private Integer id;
private String name;
#JsonUnwrapped
private Image primaryImage;
}
public class Image {
private String zoomUrl;
#JsonView(JViews.Product.class)
private String standardUrl;
}
#JsonUnwrapped annotation flattens your nested object into Product object. And JsonView is used to filter accessible fields. In this case, only standardUrl field is accessible for Product view, and the result is expected to be:
{
"id": 32,
"name": "[Sample] Tomorrow is today, Red printed scarf",
"standard_url": "url"
}
If you flatten your nested object without using Views, the result will look like:
{
"id": 32,
"name": "[Sample] Tomorrow is today, Red printed scarf",
"id":1,
"standard_url": "url",
"zoom_url":"",
...
}
Jackson provided #JsonUnwrapped annotation.
See below link:
http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonUnwrapped.html