How to convert object of any class to a Map in Java? - java

In Spring MVC we have to use org.springframework.ui.Model instance to pass model to a view. It is not strongly-typed and we have to dynamically build the object like this:
model.addAttribute("departmentID", departmentID);
model.addAttribute("departmentName", departmentName);
model.addAttribute("employees", employees);
However, I came from ASP.NET MVC, where I passed strongly-typed objects to a view, and I had ViewDepartment class which had departmentID, departmentName and employees fields, and I simply passed instance to a view. Here it doesn't work, but I still need to use ViewDepartment class, because I occasionally send it as response to AJAX-requests.
So, to get this working in Spring MVC, I need to translate ViewDepartment object to instance of org.springframework.ui.Model, one way is to build org.springframework.ui.Model from HashMap:
Model.addAllAttributes(Map<String,?> attributes)
The question is, how to build Map<String,?> attributes from instance of ViewDepartment class? Creating HashMap object and manually setting each attribute from each property is not option, because it`s not DRY. I need some way to do this with any class, because I have other model classes in other controllers.
Or, may be, someone can tell another solution to this task, related to Spring MVC specifically.

you could use reflection to get a map of all fields and their values. Be aware, that this gets complicated, if you have nested structures, but with the given example it should work,
public Map<String, Object> toMap( Object object ) throws Exception
{
Map<String, Object> map = new LinkedHashMap<>();
for ( Field field : object.getClass().getDeclaredFields() )
{
field.setAccessible( true );
map.put( field.getName(), field.get( object ) );
}
return map;
}

Related

Create Generic method for mapping of entity to dto in spring boot

I have written a method which map the entity to dto using modelMapper, now I want to reuse it for other entities, so I want to transform the below method into generic type.
private VehicleImageAndLayoutDTO mapToDto(MsilVehicleLayout msilVehicleLayout) {
log.info("MsilVehicleLayoutServiceImpl::mapToDto::START");
this.modelMapper = new ModelMapper();
TypeMap<MsilVehicleLayout, VehicleImageAndLayoutDTO> propertyMapper = this.modelMapper
.createTypeMap(MsilVehicleLayout.class, VehicleImageAndLayoutDTO.class);
// propertyMapper.addMappings(skipFieldsMap);
propertyMapper.addMappings(mapper -> mapper.using(v -> Boolean.TRUE).map(MsilVehicleLayout::getId,
VehicleImageAndLayoutDTO::setMsil));
VehicleImageAndLayoutDTO imageAndLayoutDto = this.modelMapper.map(msilVehicleLayout,
VehicleImageAndLayoutDTO.class);
return imageAndLayoutDto;
}
Now the problem is that, it is tightly coupled because of below line,
propertyMapper.addMappings(mapper -> mapper.using(v -> Boolean.TRUE).map(MsilVehicleLayout::getId,
VehicleImageAndLayoutDTO::setMsil));
because i am using model mapper, but few properties I want to set with static values, so the above code is nothing, but setting the setMsil method of VehicleImageAndLayoutDTO with Boolean.TRUE.
Now the first argument MsilVehicleLayout::getId is just like a mock argument because mapper.map() expecting two argument, so first argument is just mock one and has nothing to do with VehicleImageAndLayoutDTO::setMsil
Now, I want to figured out a way to either isolate these dependency out of this snippet or is there a way to leverage the Reflection API in order to set the custom values ??
this is the problem statement which I am expecting the answer for, please help

How to convert an Object to a custom DTO

Im having some issues trying to convert a List of Object to a custom DTO. However, all the answers that i found are focused on converting Entities or POJ0s to DTO. How can i do this in either some kind of iteration, or even manyally accesing all the Object properties?
Right now i have something like this that throw some casting errors, but idk if changing datatypes would work or if i should try something else.
List<Object> ls = myDAO.getSomethingFromDB();
List<MyDTO> ls2 = new ArrayList<MyDTO>();
for(Object o : ls){
ls2.add((MyDTO) o);
}
Also, first StackOverflow questing, sorry if im asking something dumb or in a bad way.
You can't directly convert Object to MyDTO until unless myDAO.getSomethingFromDB(); is returning list of MyDTO
Learn more about ClassCastException here:
https://docs.oracle.com/javase/8/docs/api/java/lang/ClassCastException.html
Explanation of ClassCastException in Java
if you really want to convert to MyDTO then you need to create new MyDTO object then set the values to this object.
There are probably a lot of ways to do this, here is one of those ways using Java Streams:
List<MyObject> objects = // Get objects from database
List<MyDto> dtos = objects.stream().map(myObj -> {
MyDto newDto = new MyDto();
// Set your properties here, in this example i'm setting a name and description:
newDto.setName(myObj.getName());
newDto.setDescription(myObj.getDescription());
// Repeat for every property
return newDto;
})
.collect(Collectors.toList());
I am using the map function to create a new DTO with the same properties as my object in the list.

Bind JSON response with dynamic keys to Java object

I have a json response coming from MongoDB and in its current form I have a pojo like below to bind these month field values:-
#JsonProperty("Feb-2017")
private Float feb2017;
The problem is that these month names change with time and those values will no longer be bound to the java object.The POJO in turn is an attribute of two other objects that represent this json. I cannot change the json structure in the Db and have tried creating this pojo at runtime following this answer but I cannot figure out how to reference this object across other POJOs .
Is there any other way I could approach this problem?
Thanks.
In your POJO, add a class member as follows:
private Map<String, Object> months = new HashMap<>();
Then create a method annotated with #JsonAnySetter:
#JsonAnySetter
public void set(String key, Object value) {
months.put(key, value);
}
This method works as a fallback handler for all unrecognized properties found in the JSON document.

Best practice generating object for response

I have rest server with spring.
There is a lot of requests where one of the params is fields fields is the set of fields that server should return in response. like: /?fields=[id,name] and server should return JSON object with both fields
I would like to know what is the best practice for generating such response.
We do it like this:
private Map<String, Object> processBook(BookEntity book, Set<String> fields, String locale){
Map<String, Object> map = new HashMap<String, Object>();
//..
if(fields.contains(ID)){
map.put(ID, book.getId());
}
if(fields.contains(ISBN)){
map.put(ISBN, book.getIsbn());
}
if(fields.contains(DESCRIPTION)){
if(locale.equals(UserLocale.UK)) map.put(DESCRIPTION, book.getDescriptionUa());
else if(locale.equals(UserLocale.RU)) map.put(DESCRIPTION, book.getDescriptionRu());
else map.put(DESCRIPTION, book.getDescriptionEn());
}
//..
return map;
}
Maybe there is much better alternative?
Note that in your case you obtain all data from DB - fully filled BookEntity object, and then show only requested fields.
In my opinion it'd be "much better alternative" to delegate field list to appropriate downstream integration call and get BookEntity object only with necessary fields. Then mentioned above method will reduce to just one line, your DB responses will be more lightweight, so it will bring simplicity and optimization gain to your system.
Any adequate DB provides such functionality: SQL or NoSQL, etc.
P.S. Plus standard approach of Object to JSON mapping such as Jackson or GSON at top level.
Instead of having a Map, you could have and object with the attributes you need and set them, instead of adding to map.Then you can use Google's Gson to transform your object into a Json object.Take a look at this quick tutorial.
One approach is to have an asMap function.
Map<String, Object> map = book.asMap();
map.keySet().retainAll(fields);

Dynamically creating a JSF form with java reflection

I'm trying to create a simple crud form to insert data into a database with hibernate, without knowing what the object type is. The ultimate goal is to only have one insert form for every table in the database. So far i get the methods that the current object has, check to see if it has any set methods and create a text input for every field that has a set.
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
HtmlPanelGrid hpg = (HtmlPanelGrid) viewRoot.findComponent("panel");
for (Method method : declaredFields) {
String name = method.getName();
if (name.contains("set")) {
HtmlOutputText hot = new HtmlOutputText();
HtmlInputText hit = new HtmlInputText();
hot.setValue(name.substring(3));
try {
hit.setValue(newObject.getClass().getMethod(name, String.class));
} catch (Exception ex) {
Logger.getLogger(ReflectController.class.getName()).log(Level.SEVERE, null, ex);
}
hpg.getChildren().add(hot);
hpg.getChildren().add(hit);
}
}
Here newObject is the object that is going to be inserted into the database later with hibernate. My problem is this:
How do assign a certain field from that object to the text input that is being created at the moment. So far if I put the method in the value like I'm doing above, it will just print out the method in the value attribute for that input. what i want is that when this form is submited, for to assign the value in that text box to the property with that name.
I can give you a partial answer - You need to create a ValueExpression dynamically
Application app = FacesContext.getCurrentInstance().getApplication();
hit.setValueExpression("value", app.getExpressionFactory().createValueExpression(FacesContext.getCurrentInstance().getELContext(), "#{bean.item}", Item.class));
The hard part will be creating the valueExpression that will actually map to a field within your object's value. That requires a great deal more thought but you will for sure need the dynamic valueExpression. As written, this will result in the execution of your bean's setItem();method with a parameter of type Item. You will require something a little more complex.
In JSF, binding input components to properties is accomplished with EL-expressions. You can create one programmatically as Steve shows, but that syntax is really ugly. On a related note, programmatic manipulation of the component tree is a rather unorthodox way of using JSF. The orthodox way to tackle your requirement would be something like:
<ui:repeat var="prop" value="#{genericEditorBean.propertyNames}">
<h:outputLabel value="#{prop}" for="input"/>
<h:inputText id="input" value="#{genericEditorBean.object[prop]}"/>
</ui:repeat>
where
public List<String> getPropertyNames() {
List<String> propertyNames = new ArrayList<>();
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
propertyNames.add(pd.getName());
}
return propertyNames;
}
(There really is no reason to reimplement scanning for Java Bean properties when the Java API offers a class for that very purpose. Unlike your home-grown version, this will also handle properties inherited from a super class ...)
I once used an open-source library named MetaWidget to do this.
It was a few years ago, but it worked well and was easy to set up.
It looks like the project is still active:
http://metawidget.sourceforge.net/index.php

Categories