I have a situation, where I need to map multi objects (in a flat structure) into one object (an hierarchy object) in Java using ModelMapper.
For example,
class Person{
String name;
int age;
}
class Address{
int streetSumber;
String streetName;
String city;
}
class Phone{
String type;
String number;
}
What I want to get out from the model mapper is something like
class PersonDTO{
String name;
int age;
class AddressDTO{
int streetSumber;
String streetName;
String city;
}
class PhoneDTO{
String type;
String number;
}
}
Is there a way to simple achieve this?
Thanks
You must create a PersonWrapper
class PersonWrapper {
Person person;
Address address;
Phone phone;
}
and map PersonWrapper to PersonDTO, for example
PropertyMap<PersonWrapper, PersonDTO> orderMap = new PropertyMap<Order, OrderDTO>() {
protected void configure() {
map().setName(source.getPerson().getName());
....
}
};
Related
I have an input object as
class Person {
private String name;
private String email;
private String phone;
private Address address;
public static class Address {
private String city;
private String pincode;
private String street;
private AddrDetails details;
public static class AddrDetails {
private String state;
private String country;
}
}
}
I am using vavr Validations to validate the input
public static Validation<Seq<ConstraintViolation>, PersonDetailsModel> validatePerson(PersonDetailsRequest request) {
Validation
.combine(
validateName("name", request.getName()),
validateEmail("email", request.getEmail()),
validatePhone("phone", request.getPhone()),
validateAddress(request.getAddress())
).ap((name, email, phone, address) -> new PersonDetailsModel(name, email, phone, address);
}
public static Validation<Seq<ConstraintViolation>, Person.Address> validateAddress(
Person.Address request) {
return Validation
.combine(..
).ap((..) -> new Person.Address(..);
}
In the second function, it returns Seq of ConstraintViolation while validatePerson expects only ConstraintViolation which is why it is failing although I have to add one more level of nesting of validations for AddrDetails. How to handle nested objects validations with this approach.
I am not sure about how shall I go ahead?
In our project we call .mapError(Util::flattenErrors) after .ap. I have the feeling that there is a better way, but this at least solves the nesting.
The method in the Util class looks like this :
public static Seq<ConstraintViolation> flattenErrors(final Seq<Seq<ConstraintViolation>> nested) {
return nested
.flatMap(Function.identity())
.distinct(); // duplicate removal
}
I have entity class
public Class StudentEntity{
private int id;
private String name;
private AddressEntity address;
private ProfileEntity profile;
//getter setter
}
public Class StudentDTO{
private int id;
private String name;
private AddressDTO address;
private ProfileDTO profile;
//getter setter
}
when I use BeanUtils.copyProperties(); (from spring/apache common) it copies the id and name alone. How to copy the address and profile also?
If custom util has to be written, could you please share the snippet?
BeanUtils, cloning OR serialization would not work here as the inner data types are different. I would suggest you to set the fields of StudentDTO manually. You could use conversion constructor for AddressDTO and ProfileDTO. Copy constructor is the legal name, but since we are converting type also, better name would be a conversion constructor instead.
An example of conversion constructor in JDK is ArrayList(Collection<? extends E> c) , i.e. https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#ArrayList-java.util.Collection- which generates an ArrayList from any Collection object and copy all items from Collection object to newly created ArrayList object.
Example:
StudentEntity studentEntityObj = new StudentEntity();
studentEntityObj.setId(1);
studentEntityObj.setName("myStudent");
AddressEntity addressEntityObj = new AddressEntity();
addressEntityObj.setCity("myCity");
studentEntityObj.setAddress(addressEntityObj);
// All above lines would be taken care of already (i.e. data is filled from DB)
StudentDTO studentDTOObj = new StudentDTO();
// Call conversion constructor
AddressDTO addressDtoObj = new AddressDTO(addressEntityObj);
studentDTOObj.setAddress(addressDtoObj);
studentDTOObj.setId(studentEntityObj.getId());
studentDTOObj.setName(studentEntityObj.getName());
System.out.println(studentDTOObj.toString());
where AddressDTO (OR ProfileDTO for that matter) including a conversion constructor looks like:
public class AddressDTO {
private String city;
// Conversion constructor
public AddressDTO(AddressEntity a) {
this.city = a.getCity();
}
#Override
public String toString() {
return "AddressDTO [city=" + getCity() + "]";
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
prints
StudentDTO [id=1, name=myStudent, address=AddressDTO [city=myCity]]
You can try to use SerializationUtils.clone(). This method deep clone your object. But you should mark your objects as Serializable.
https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/SerializationUtils.html#clone(T)
In my Android project I have two types of response where both response are identical except two keys.
Response 1
{"fullName":"William Sherlock Scott Holmes","address":"221B Baker Street, London, England, UK","downloads":642,"rating":3,"repos":["https://link1","https://link2","https://link3"]}
Response 2
{"name":"Sherlock","city":"London","downloads":642,"rating":3,"repos":["https://link1","https://link2","https://link3"]}
If you see the responses only two key names are changing fullName/name and address/city
I don't want to create one more pojo for other response. My question is: is it possible to use only one Pojo to read both responses?
public class AccountInfo {
private String name;
private String city;
//other objects
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
//other setters and getters
}
Any help will be appreciated...
You can annotate the members to accept values from two different json names using the #SerializedName annotation:
#SerializedName(value = "name", alternate = {"fullName"})
private String name;
#SerializedName(value = "city", alternate = {"address"})
private String city;
Either named element can then be placed into the members that are annotated like this.
UPDATED :
#SerializedName alternate names when deserializing is added in Version 2.4
Yes, you can totally use one POJO class for deserializing both responses. Your POJO class will contain keys from both responses.
public class Response {
private String name;
private String city;
private String fullName;
private String address;
private Integer downloads;
private Integer rating;
private List<String> repos ;
}
But when using the Response class, be careful that for first response, the name and city will be null, and for the second one, the address and fullname.
Yeah you can do that in a single POJO. Try this:
public class POJO {
#SerializedName("name")
public String name;
#SerializedName("city")
public String city;
#SerializedName("fullName")
public String fullName;
#SerializedName("address")
public String address;
#SerializedName("downloads")
public Integer downloads;
#SerializedName("rating")
public Integer rating;
#SerializedName("repos")
public List<String> repos = new ArrayList<String>();
}
While parsing you have to check values for null. For eg -
While Parsing Response 1: name and city variables will be null
While Parsing Response 2: fullname and address will be null
Note : Try checking values for null before using else you'll get nullpointerexception
Define all possible fields in your POJO Class like
public class AccountInfo {
private String name;
private String city;
private String fullname;
private String address;
}
While performing operation check for null in those feilds
I'm trying to learn about serialization and encountered the following problem:
I have an implementation of a customer that looks somewhat like this.
private static customerCount = 0;
private String customerID;
private String name;
private String street;
private String city;
private String postcode;
private String type;
I'm trying to serialize / deserialize an Arraylist
In the constructor, the ID will be created like this:
private Customer(...){
this.customerID = "ID" + customerCount;
customerCount++;
}
The serialization process works, however, all the IDs are set to ID0 when I deserialize.
Can anyone help resolve this problem?
Update: Alright, I just found out that static fields wont be serialized. How can I "model" the ID of a customer so I can serialize it? I need to have a unique value to create IDs for customers.
Here's a solution that combines the factory with the list that keeps track of customer count.
The customer class has a protected constructor, forcing you to build them through another means within the same package.
public class Customer implements Serializable {
private String customerID;
private String name;
private String street;
private String city;
private String postcode;
private String type;
protected Customer(String customerID,
String name,
String street,
String city,
String postcode,
String type) {
this.customerID = customerID;
this.name = name;
this.street = street;
this.city = city;
this.postcode = postcode;
this.type = type;
}
}
Now within the package, create a list wrapper like this:
public class CustomerList {
private int customerCount = 0;
private List<Customer> customers = new ArrayList<>();
public boolean addCustomer(String name,
String street,
String city,
String postcode,
String type) {
Customer customer = new Customer("ID" + customerCount++,
name,
street,
city,
postcode,
type);
return customers.add(customer);
}
}
This class then takes care of constructing the new customer, and provides a unique ID.
Edit: Just noticed that you now also have the upside of making the CustomerList class serializable as well. Then you can load it and still have an accurate customer count for adding additional uniquely ID-ed customers.
Usually, you would like to serialize only attributes and their values, not the logic from the class. Logic should happen before serialization or after deserialization.
I have the following xml
<MyPojo>
<name>Jason</name>
<age>25</age>
<meta>
<occupation>Engineer</occupation>
</meta>
</MyPojo>
I need to deserialize it to the following POJO:
public class MyPojo {
private String name;
private int age;
private String occupation;
}
The problem here is that occupation is wrapped within meta element
You need one more object:
public class MyPojo {
private String name;
private int age;
private Meta meta;
}
public class Meta{
private String occupation;
}
My idea is to replace occupation with an own class. Something like myMeta or whatever you want to call it(be aware in your case like the xml says: meta). This class should cotain the field occupation:
public class Meta
{
private String occupation;
}
After that you only have to add a new field of your new class e.g. myMeta to myPojo. Something like this:
public class MyPojo
{
private String name;
private int age;
private Meta meta;
}
this should avoid
that occupation is wrapped within meta element
Hope that helps!