I'm looking for an automated way to read a property file and invoke corresponding setters on a Java object.
#property file
person.firstName=firstName
person.age=11
person.address.cityName=cityName
...
Please bear with the following example object model.
public class Person{
// below have getters & setters
private String firstName;
private int age;
private Address = new Address(); // not null
}
public class Address{
//below have setters and getters
private cityName;
...
}
What I'm looking for
Person personObj = new Person();
MagicTransformer mt = new MagicTransformer();
mt.doMagic("filePath", personObj);
//now the personObj has all the properties set from the file.
It'd be great if the framework is clever about transforming standard types: primitives, strings, enums, lists (primitives & strings), maps (primitives & strings)
I explored before posting. Any search related to reading 'property files' had returned Spring's property placeholder, etc. I might have missed a resource. It'd be great if you could name a couple of frameworks.
Don't know about a single lib to do that all but it should be fairly trivial to:
read the property file into a java Map (this should be fairly straightforward?)
use the above and convert it to your POJO using either http://commons.apache.org/proper/commons-beanutils/ or using https://github.com/FasterXML/jackson (usin ObjectMapper)
How about storing the data in xm; and using xmlbeans
A simple example is
File xmlFile = new File("c:\employees.xml");
// Bind the instance to the generated XMLBeans types.
EmployeesDocument empDoc =
EmployeesDocument.Factory.parse(xmlFile);
// Get and print pieces of the XML instance.
Employees emps = empDoc.getEmployees();
Employee[] empArray = emps.getEmployeeArray();
for (int i = 0; i < empArray.length; i++)
{
System.out.println(empArray[i]);
}
Related
I have two classes (entity and DTO)
public class Deliver {
private Long id;
private String uri;
private Instant moment;
private DeliverStatus status; // enum PENDING,ACCEPTED,REJECTED
private String feedback; // feedback about received task
private Integer correctCount; // nr of correct questions
private Enrollment enrollment;
private Lesson lesson;
// constructors, getters and setters..
public class DeliverRevisionDto {
private DeliverStatus status;
private String feedback;
private Integer correctCount;
// constructors, getters and setters..
The goal is pretty simple, update the entity fields conveyed by Dto class I have the following code at Service layer (Spring Boot version 2.4.4):
#Service
public class DeliverService {
#Autowired
private DeliverRepository deliverRepository;
#Autowired
private ModelMapper modelMapper;
#Transactional
public void saveRevision(Long id, DeliverRevisionDto dto) {
Deliver deliver = deliverRepository.getOne(id);
System.out.println("BEFORE MAPPING: " + deliver.toString()); // # debug purpose
deliver = modelMapper.map(dto, Deliver.class);
// # debug purpose
TypeMap<DeliverRevisionDto, Deliver> tm = modelMapper.getTypeMap(DeliverRevisionDto.class, Deliver.class);
List<Mapping> list = tm.getMappings();
for (Mapping m : list)
{
System.out.println(m);
}
System.out.println("AFTER MAPPING: " + deliver.toString()); // # debug purpose
deliverRepository.save(deliver);
}
}
The console output is:
BEFORE MAPPING: Deliver [id=1, uri=``https://github/someone.com``, moment=2020-12-10T10:00:00Z, status=PENDING, feedback=null, correctCount=null, enrollment=com.devsuperior.dslearnbds.entities.Enrollment#7e0, lesson=com.devsuperior.dslearnbds.entities.Task#23]`
`PropertyMapping[DeliverRevisionDto.correctCount -> Deliver.correctCount]`
`PropertyMapping[DeliverRevisionDto.feedback -> Deliver.feedback]`
`PropertyMapping[DeliverRevisionDto.status -> Deliver.status]`
`AFTER MAPPING: Deliver [id=null, uri=null, moment=null, status=ACCEPTED, feedback=Muito bem cabra, tarefa aceita., correctCount=5, enrollment=null, lesson=null]
The mapping of the 3 fields in DTO is done correctly, BUT all the other fields of my entity are set to null. I know that I can skip fields according http://modelmapper.org/user-manual/property-mapping/
The problem is that I don´t want to couple the code with specific field names/getters/setters, that´s the reason I´m using ModelMapper. I wonder if there is any configuration that, upon mapping the modelmapper object says "Hey, the TARGET class have way more fields than the SOURCE class, I will left them untouched unconditionally (meaning I don´t need to say what fields are).
I'm trying to map fields between 2 classes with different set of fields (some are the same), and when I map the class with smaller set of fields to the one with bigger set of fields, the mapper set fields that don´t match with "null", I want these fields untouched (with original values) without I telling which one they are, after all, the mapper knows which ones match.
ModelMapper documentation is not the best part of that framework. Let us see what happens in your code.
Here you fetch the entity to be updated from the repo:
Deliver deliver = deliverRepository.getOne(id);
and log it having all the fields as should be. However this line:
deliver = modelMapper.map(dto, Deliver.class);
does a re-assignment to your variable deliver. This method creates a new instance of Deliver class and assigns it to variable deliver so discarding the entity fetched from repo.
This new instance will have all the fields that are not existing or not set in DTO null.
This is the API doc that my IDE provides, fotr these two different methods:
String org.modelmapper.ModelMapper.map(Object source, Class destinationType)
Maps source to an instance of destinationType. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destinationType then one is created.
Versus
void org.modelmapper.ModelMapper.map(Object source, Object destination)
Maps source to destination. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destination.getClass() then one is created.
It might not be clearly stated that the first method actually creates a new instance based on the type (Class) passed but it should be clear that ModelMapper cannot alter some arbitrary variable just by knowing the type. You need to pass the variable to alter as method parameter.
Let's say we have an Object A defined like this:
public class ObjectA {
private Attribute a1;
private Attribute a2;
private Attribute a3;
}
For some reason, I need to create a second object B with only the first two attriutes of the Object A :
public class ObjectB {
private Attribute a1;
private Attribute a2;
}
So my question is: what is the best approach to copy an Object A to an Object B ?
I've been copying the attributes by getters and setters one by one but something tells me there must be a better way to do this ! Especially when the object will have a lot of attributes, I have to write lines and lines of code just to copy all of them to the second Object B ...
Thanks a lot :)
EDIT: I've been being alerted by a "possible duplicate of another question" : How do I copy an object in Java?
My question is slightly different in a way that I'm dealing with 2 different objects who just share the same attributes but not totally !
Try libraries like Dozer or BeanUtils
To expand on my comment:
Using Dozer it can be as easy as:
Mapper mapper = new DozerBeanMapper();
ObjectA source = new ObjectA();
ObjectB target = mapper.map(source , ObjectB.class);
or if your target class doesn't have a no-arg constructor:
ObjectA source = new ObjectA();
ObjectB target = new ObjectB(/*args*/);
mapper.map(source, target );
From the Documentation (emphasis by me):
After performing the Dozer mapping, the result will be a new instance of the destination object that contains values for all fields that have the same field name as the source object. If any of the mapped attributes are of different data types, the Dozer mapping engine will automatically perform data type conversion.
What you need is Object mappers. Try Orika or Dozer.
The objects need not be of the same type. While mapping if it finds the attributes of the same name and type, it automatically maps it.
MapperFacade mapper = mapperFactory.getMapperFacade();
UserDTO userDTO = new UserDTO();
userDTO.setName("xyz");
..
User user = mapper.map(userDTO, User.class);
You can also customize if you have to map different attribute names.
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(UserDTO.class, User.class)
.field("name", "username")
.byDefault().register();
mapper = mapperFactory.getMapperFacade();
...
User user = mapper.map(userDTO, User.class);
I am looking for a nice way to group a list of objects by multiple class attributes. The Google guava library provides a feature to group by a single attribute. I haven't found a utility yet that offers what I am looking for.
See below. I have encapsulated the grouping attributes into its own class Grouping. However, this need not be necessary if a better solution exists.
A class that contains grouping attributes.
Class Grouping{
private String key1;
private String key2;
private String key3;
//getters, setters
}
Class representing a type that would need to be grouped.
Class Groupable{
private Grouping grouping;
private String x;
private String y;
private String z;
// getters, setters
}
What I need is a Map that contains as key, a Groupable object with unique set of keys. And a list of Groupable objects as corresponding grouped items.
Map<Grouping, List<Groupable>> groupings;
Any and all suggestions welcome!
Answering my own question based on #JBNizet and #JohnBollinger's comments.
I use Eclipse IDE, so all I needed to do is to go to Source -> 'Generate hashCode() and equals()..' and invoke the same on my Grouping class to include all attributes in the overridden implementation. There on, guava library can be used in its regular way as below:
Function<Groupable, Grouping> groupFunction = new Function<Groupable, Grouping>(){
#Override
public Grouping apply(Groupable groupable) {
return groupable.getGrouping();
}
};
ImmutableListMultimap<Grouping, Groupable> groupings = Multimaps.index(lsGroupable, groupFunction);
groupings gives me what I need. lsGroupable is a list of Groupable objects.
I am trying to serialize a JSON object using Jackson and save into a mysql database using hibernate. All fields of my POJO class are able to be serialized except for any field that isn't a primitive.
public class Teacher {
private Set<Student> students;
private int id;
// getters and setters
}
In this case it would fail on students, creating an infinite recursive loop through the reference chain. I can stop it with #JsonIgnoreProperty but I want this field to be serialized. I am serializing my object like so:
ObjectMapper mapper = new ObjectMapper();
Teacher myTeacher = new Teacher();
mapper.writeValueAsString(teacher);
The only workaround I can think of is appending a string to the end of teacher while still ignoring the property but I am not sure if I will be able to read students as a JsonNode from the tree if I do this.
A way around this would be to use a pure Array or an ArrayList which are serialized fine with Jackson.
For example, I can serialize a class with all these parameters :
public class Map{
private short [][] someMapType;
private short [][] someOtherMap;
private ArrayList<Mill> someMills, otherMills;
private ArrayList<OtherPOJO> myPOJOList;
private String action = "myDefaultAction";
...
}
Where Mill and OtherPOJO are class with not much more than a couple of arrays and other primitives : pure POJOs.
It works fine both ways with Jackson and MongoJack (jackson serializer for MongoDb).
If you can't get away from the set than you have to understand properly what is the fundamental data structure in a set. This should help but you are probably already aware of that.
A way to work around this structure limitation would be to create non-dumb getters and setters. The main disadvantage behind this method is that you run one more for loop over all your elements every time you serialize or de-serialize. This might slightly reduce performance.
The getter is fairly simple :
public Student[] getStudents(){
return this.students.toArray();
}
And the setter is also pretty trivial :
public void setStudents(Student[] students){
this.students = new Set<Student>(); // Or anything that builds the right Set for you
for(int i = 0; i < students.length; i++){
this.students.add(students[i]);
}
}
I wrote it quickly, let me know if there is any bug.
I hope it helps!
Found a decent workaround:
#JsonIgnore
public Set<Student> getStudents() {
return students;
}
#JsonProperty("Students")
public String getStudentsForJson() {
String[] studentNames = new String[this.students.size()];
int i = 0;
for(Student student : this.students) {
studentNames[i] = student.getName();
i++;
}
return StringUtils.join(studentNames, ", ");
}
This saves all student names as one string which I'm able to easily serialize and deserialize as a field.
There is a question on my mind for a while. Let's say we have the following classes:
public Class Person{
String name;
String address;
String description;
}
public Class PersonFacade{
String name;
String address;
String desc;
}
as you can see the only difference between these two classes are the name of one variable. My question is what is the best way to write a helper class to map the values of one object to another object. Let's assume we have the following:
Person person = new Person();
person.name="name1";
person.address="address1";
person.description="description1";
I want to write a class that is supposed to do the following (let's call it Transformer class)
PersonFacade personFacade = new PersonFacade();
TransformClass.transformFrom(person, personFacade);
What I want this TransformClass.transformFrom() method to do is the follwoing:
based on the similarity of the variable names, assign the value of the variable from "FromClass" to "ToClass"
so in our case, I want it to assign personFacade.name = "name1", personFacade.address="adderss1" and personFacade.desc = "description1" (this last one seems harder to accomplish, but let's try)
Any ideas?
You can use Dozer:
Dozer is a Java Bean to Java Bean mapper that recursively copies data
from one object to another. Typically, these Java Beans will be of
different complex types.
Dozer supports simple property mapping, complex type mapping,
bi-directional mapping, implicit-explicit mapping, as well as
recursive mapping. This includes mapping collection attributes that
also need mapping at the element level.
Look at this: http://dozer.sourceforge.net/
It's a great JavaBean Mapper.
Here the "Getting Started":
http://dozer.sourceforge.net/documentation/gettingstarted.html
Perhaps you can write your own Annotation class in order to create the relationship between the classes. So, for example
public Class Person{
#MyAnnotation(id='name')
String name;
#MyAnnotation(id='addr')
String address;
#MyAnnotation(id='desc')
String description;
}
public Class PersonFacade{
#MyAnnotation(id='name')
String name;
#MyAnnotation(id='addr')
String address;
#MyAnnotation(id='desc')
String desc;
}
Then in your TransformClass, you simply need to iterate through the annotations, find a match and set the corresponding field value with the help of Reflection.