Set object fields from HashMap - java

Is there a library that can do the following?:
Given an Object and a HashMap, it enumerates the keys of the Hashmap and looks up the setters for these keys in the Object and sets the associated values. Something looking like that:
public Object setData(Object object, HashMap<String, Object> fields) {
for (Entry<String, Object> entry : fields.entrySet()) {
Method m = object.getClass().getMethod("set" + entry.getKey(), entry.getValue().getClass());
if (m != null) {
m.invoke(object, entry.getValue());
}
}
return object;
}
The task looks simple at the first look but there are some nuances that I hope someone has already taken care of. As you know, reinventing the wheel (the good wheel) is a bad approach.

Look at Apache Commons BeanUtils
org.apache.commons.beanutils.BeanUtils.populate(Object bean, Map properties)
Javadoc:
Populate the JavaBeans properties of the specified bean, based on the specified name/value pairs. This method uses Java reflection APIs to identify corresponding "property setter" method names, and deals with setter arguments of type String, boolean, int, long, float, and double.

Better use BeanUtils class:
public Object setData(Object object, HashMap<String, Object> fields) {
for(Entry<String, Object> entry : fields.entrySet()) {
BeanUtils.setProperty(object, entry.getKey(), entry.getValue());
}
return object;
}

I have a BeanAsMap class that I wrote a long time ago. The method asMap returns a Map that is a view on a Java bean (POJO). You can call putAll on that Map, passing it the Map that you want to copy data from.
Feel free to use my code mentioned above.
Example:
MyClass bean = ...;
Map<String, Object> inputData = ...;
Map<String, Object> view = BeanAsMap.asMap(bean);
view.putAll(inputData);

BeanUtils is fine.
But, as good practice, i would not write code that use reflection. Or as the last solution i have, if none other has been found.
This code cannot be tracked in IDE like Eclipse (no call hierarchy), making the developer think that the setters are never called. He can break your code and that will still compile.
Too high level of abstraction like this makes the code difficult to understand.
Code that is being obfuscated will be broken by the obfuscator itself when writting such things.
Best solution would be to rethink the use of reflection to set the object fields.

Check out http://commons.apache.org/beanutils/, in particular BeanUtils.populate():
http://commons.apache.org/beanutils/v1.8.3/apidocs/index.html

Related

How to place a Map into another Map in Java

I'm doing a personal project.
After a lot of maps, I came to the point where I want to enter a map inside another map... Let me explain better.
I have this Map atPropostas which have information about a person, but at some time of the program, I want to add to that person a boss email.
private Map<String, Long> atPropostas;
private Map<String,Map<String,Long>> atOrientadores;
To add that boss email, I've done this:
for (Map.Entry<String, Long> atprop : atPropostas.entrySet()) {
for (Map.Entry<String, Proposta> prop : propostas.entrySet()) {
if (prop.getValue().getTipo().equals("T2") && propostas.containsKey(atprop.getKey())) {
atOrientadores.put(prop.getValue().getEmailDocente(), atprop);
}
}
}
But, atprop value, inside atOrientadores.put() it produces an error:
java: incompatible types:
java.util.Map.Entry<java.lang.String,java.lang.Long> cannot be
converted to java.util.Map<java.lang.String,java.lang.Long>
I have tried to cast the atprop:
atOrientadores.put(prop.getValue().getEmailDocente(), (Map<String, Long>) atprop);
And the compiler error disappears, but the App itself, doesn't working.
You can't convert Map.Entry in to a Map with casting.
Map and entry represent inherently different notions. Map consists of entries, it isn't an entry.
You can create a single entry map by using static method Map.ofEntries() available with Java 9
atOrientadores.put(prop.getValue().getEmailDocente(), Map.ofEntries(atprop));
Sidenote: it's highly advisable to use English while giving names to variables, classes, etc. in your application. It makes it easier to reason about your code.
I don't know for sure what atprop is meant to represent, but you've mentioned a person and an email. I advise you to consider creating classes that would correspond to a person and an email instead of complicating your code and dealing with nested maps.

Is there a way to map a string to a method that returns a different type depending on the string?

I want to map a String to a method that builds a certain object, but not necessarily from the same class for every String. Looking around on here a nice solution was to have a Map<String, ObjectBuilder>, ObjectBuilder<T> being an interface with an abstract method T buildObject().
I then have multiple classes, let's say Object1Builder implements ObjectBuilder<Object1>, Object2Builder implements ObjectBuilder<Object2> and so on.
I can then construct my map like so :
stringToBuilder = new HashMap<String, ObjectBuilder>(){{
put(string1, Object1Builder);
put(string2, Object2Builder);
put(string3, Object3Builder);
}};
And I can then do Object1 myObject1 = stringToBuilder.get(string1).buildObject()
Problem is, I get an error
Raw use of parameterized class 'ObjectBuilder'
in IntelliJ when I instanciate and construct stringToBuilder and I understand it has something to do with not specifying the generic of the interface ObjectBuilder when constructing the map, but I don't see how I could circumvent this. Moreover, I'm not very satisfied with the fact that I'm storing these classes in a map, I wish I could access them through the map without having the whole instance in the map.
You've probably noticed I'm quite new to Java and all this but please be sure I'm trying my best. Thank you in advance :)
What you want will never be possible without explicit casts. The reason is that there is no direct relation between the map keys (strings) and values (ObjectBuilders).
If you can switch from strings to use the T values as map keys, this can be done with a little internal casting.
First, declare your map as Map<Class<?>, ObjectBuilder<?>>. Note the two wild-cards; the compiler cannot help us with enforcing that the keys and the values have the same generic type. That's what we need to do ourselves.
Next, initialize it as necessary. I dislike the anonymous class with initializer you use, so I'll use Map.of:
Map<Class<?>, ObjectBuilder<?>> classToBuilder = Map.of(
Object1.class, Object1Builder,
Object2.class, Object2Builder,
Object3.class, Object3Builder,
);
Finally, we need a method to get the builder:
#SuppressWarnings("unchecked")
private <T> getBuilder(Class<T> type) {
// Omitted: presence check
return (ObjectBuilder<T>) classToBuilder.get(type);
}
This can now be used as follows:
Object1 object1 = getBuilder(Object1.class).buildObject();

Keeping track of pairs of Strings

My current project requires me to utilize pairs of Strings for various use cases. I am currently using the Apache Lang3 Pair tuple to handle this case. The current implementation looks like the following
private List<Pair<String, String>> configParameters;
private List<Pair<String, String>> sensorReadingMeasures;
After discussion with the rest of the team it was decided that because we would need to expose these pairs of strings it would be best to find native Java solution.
I have attempted to find a solution using the AbstractMap in the java util library but I haven't had much luck being able to instantiate it, and a google search hasn't provided much information on helping me along.
I have attempted to recreate the above by doing,
AbstractMap<String, String> pair = new AbstractMap<String, String>();
I would be able to pull the required information using the keyset() function and pulling the needed information out by the get() function for each key value.
Beyond my instantiation problem this seems like a terribly inefficient way to get the same functionality, and I am curious if there is a better option. If there isn't can someone please provide an example on how to instantiate an AbstractMap appropriately.
If one of the two strings in each pair is unique, as your suggestion to use an AbstractMap seems to suggest, you may want to expose an API that returns Map<String,String>, and use an implementation of your choice for that interface (say, LinkedHashMap<String,String>).
The users of your class would be able to pull the pairs of Strings from the Map by using Map.Entry<String,String>, rather than calling a get for each key:
Map<String,String> getAllPairs() {
...
}
...
for (Map.Entry<String,String> pair : myClass.getAllPairs()) {
// You can use pair.getKey() and pair.getValue() here
}
Can you not just use a POJO:
public class Tuple {
public String oneString;
public String otherString;
}
You can have lists of them:
public List<Tuple> tuples = new ArrayList<Tuple>();
Or you can have Collections of them that can be searched if you:
#Override
public boolean equals(Object obj) {
// Code to handle non-Tuples and null goes here
// Code to handle null oneString values goes here (if you allow that)
return ((Tuple)that).oneString.equals(oneString);
}
#Override
public int hashCode() {
// handle null oneString values here
return oneString.hashCode();
}

Java analog for PHP's StdClass

I need something like a property bag to throw key value pairs into.
I want to create list of such objects, initialize them in bean and then use the list to render table in JSF template.
I know how to do it, but I want to avoid creating some special class for that case, like OrderLineItem and using List<OrderLineItem>.
I do not want to define new class.
In PHP I could use StdClass.
StdClass is a sparsely documented class in PHP which has no predefined members.
$object = new StdClass;
$object->foo = 'bar';
But, as far as I know, Primefaces <p:dataTable> list item must be an object with getter.
If I want to reference <h:outputText value="#{item.title}" />, my list object should have getTitle() method.
Is there any walkaround in my case or I really need to define special class to make life easier?
Thanks.
When you want a simple key/value table, then the HashMap might be what you are looking for.
Map<String, String> myMap = new HashMap<>();
myMap.put("foo", "bar");
System.out.println(myMap.get("foo")); // outputs "bar"
This example matches Strings to Strings, but you can use HashMaps to map any type to any other type. You can even create a Map<Object, Object> to create a weakly-typed map which maps anything to anything. But for most use-cases you would rather use a more specialized form.
What you need is a Map.
You can store key-value pairs in it pretty easy:
Map<KeyClass, ValueClass> myMap = new HashMap<KeyClass, ValueClass>();
Use the put method to put data in it. If you use simple String values it will be like this:
myMap.put("key", "value");
I don't know if I understood you well. But I think you mean SelectItem or JsfMap.
I would recommend to use an anonymous class:
return new HashMap<String, String>() {
{
this.put("key", "value");
}
};

Is it possible to have a hashmap with 4 objects?

Can I have an hashMap with say ID as my key and info, name, quantity as my values?
ok, say I have a class (Products) already that sets my variables, getters and setters. In my Invoice class, which is where the hashMap would be. Would I put like:
private HashMap<String, Products> keys = new HashMap<String, Products>
I'm not quite sure how to access the HashMap though. Say I implement a class that allows me to add and remove invoices from the HashMap, I do not know what the values would be:
keys.put(??value of id??,??not sure what goes here??);
Sure. Make another class that contains your info, name and quantity and put that as the value of your HashMap.
No, but the best way is to wrap the information you want to keep in the map in a class:
public class Info {
private String info;
private String name;
private int quantity;
...
public Info(String info, String name, int quantity) {
...
}
}
Then do this to put something in the map:
Info info = new Info("info", "name", 2);
Map map = new HashMap<Integer, Info>();
map.put(22, info);
And do this to get something out:
Info info = map.get(22)
How about HashMap<Integer, ArrayList<String>> ?
UPDATE: Please try to avoid this, this is a better approach.
Not exactly.
A Map defines a strictly 1 to 1 relationship between keys and values. One key in the map has one value.
If you want to associate multiple values with one key you need to do one of the following:
Define a Values class to represent the values as a single object; e.g. as per #Starkey's and #Javed's answers. Then the map becomes a Map<String, Values> (assuming that the key type is String).
Define the map as a Map<String,List<Object>> or Map<String,Object[]> and represent the values as an untyped list / array
Define the map as a Map<String,Properties> or Map<String,Map<String,Object>> and represent the values as the Java equivalent of an associative array.
Of these, the first option is both the safest (smallest chance of runtime errors), the most efficient and the best style.
(Aside: an Apache commons MultiMap might be considered as another possibility, but the conceptual model and APIs don't really match this use-case.)
Sure. Depending on how flexible your datastructe is you can use a Hashmap a la:
HashMap<IdType, List<String>>, with IdType String or Integer, depending on the Keys you like to use.
HashMap<IdType, String[]>
HashMap<IdType, YourObjectType>, with YourObjectType beeing a Object you defined yourself, holding the values you like
YourObjectType can of course be anything you can define as an Object. Also another HashMap if you like.
One of the concerns while using a Map would be use of hardcoded keys. If the key is a string, and the key changes. Can consider using a constant instead of a hardcoded string.
Having a dedicated class has the benefit of compiler to check for name changes. However, as mentioned in the earlier comments.. It can become a concern...
In my opinion both are feasible. We need to weigh which option is better depending on the situation
Create an object that encapsulates the four together. Something like:
public class Foo {
private String s1;
private String s2;
private int v3;
private MyObject obj1
// constructors, getters, helper functions.
}
I think MultiMap from google library could serve the purpose
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html
Multimap<String, String> map = ArrayListMultimap.create();
String key = "uniqueKey";
map.put(key, "value1");
map.put(key, "value2");
map.put(key, "value3");
System.out.println(map);//{uniqueKey=[value1, value2, value3]}
Of course, you could for example declare it like this: HashMap<Integer, HashMap<String,Object>> You use the outer hashmap to link your id with your inner HashMap, and in the inner one, you create keys "info", "name", "quantity" and associate values with them.
Of course, you could also use an ArrayList as the outer collection (it could be a better match for your ID: ArrayList<HashMap<String,Object>> that way you have indexed (id based) access to each of your "info", "name", "quantity" hashmap "records"
You could have ID as key and a List or Set (Collection in general) of objects as value.

Categories

Resources