Create an Object instance from a Map - java

I have a map with keys and string values.
This Map has been built reading a resource bundle where the data is organised in the following way:
height=160
weight=80
name=bob
And I have a class Person which has the fields: height, weight and name.
class Person{
int height;
int weight;
String name;
//..getter and setter..
}
I would like to create an instance of the class Person from the Map: height:160, weight:80, name:bob
The best would be a generic solution, or something that uses some utilities.
Do you have any idea? how can I do that in Java? or using the framework Spring?

Have a look at the Spring BeanWrapper interface and its implementations if you'd like to use something from Spring. You can use it to wrap your bean and dynamically populate your bean from a map like this:
Map<String, String> properties = new HashMap<>();
properties.put("height", "160");
properties.put("weight", "80");
properties.put("name", "bob");
BeanWrapper person = new BeanWrapperImpl(new Person());
for (Map.Entry<String, String> property : properties.entrySet()) {
person.setPropertyValue(property.getKey(), property.getValue());
}
System.out.println(person.getWrappedInstance().toString());
This will print:
-> Person [height=160, weight=80, name=bob]

The easiest way to achieve this is to use jackson's ObjectMapper class.
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> fields = ...;
Person o = mapper.convertValue (fields, Person.class);

The classic Java way is to pass the value Map as an argument to the constructor of Person and let person read the properties from the map.
This way you can have multiple ways for constructing a Person object. Either by passing arguments directly, or passing the map.
I would like to bring forward another benefit of this approach. If you do it this way, the cohesion is very high. This means that the knowledge of how to construct a Person object from a Map of values is coded within the class itself. If you would do this outside of the class and you want to construct Person objects in different locations of you program, then you would need to replicate the code for getting values from the map or abstract it into a utility method. Now you don't, and if you every would need to change the way how to construct a Person object you simply change it in one place.
import java.util.Map;
public class Person {
private static final String WEIGHT_PROPERTY = "weight";
private static final String HEIGHT_PROPERTY = "height";
private final int height;
private final int weight;
public Person(Map<String, String> map){
height = Integer.parseInt(map.get(HEIGHT_PROPERTY));
weight = Integer.parseInt(map.get(WEIGHT_PROPERTY));
}
public int getHeight() {
return height;
}
public int getWeight() {
return weight;
}
}

Map<String, String> map = ...;
int height = Integer.parseInt(map.get("height"));
int weight = Integer.parseInt(map.get("weight"));
Person p = new Person(height, weight);
Note that ResourceBundle is normally used to deal with internationalization. If you just need to read properties, then use the java.util.Properties class.

Simplifying #Jeroen Peeters post
public class Person {
Map<String, String> prop;
public Person(Map<String, String> map){
prop = map
}
public int getHeight() {
return Integer.parseInt(prop.get("height"))
}
public int getWeight() {
return Integer.parseInt(prop.get("weight"));
}
}

Related

How to serialize/deserialize object to Map

I have one specific case. I need to serialize/deserialize an object to Map<String, Object>. I have a class that looks like the following:
public class Data {
public String name;
public Map<String, Object> options = new HashMap<>();
}
I can put to this options objects of any type. For instance:
public class Option {
public int id;
...
}
public class TestOpt {
public name;
...
}
and I try to serialize and deserialize it:
public static void main(String... args) {
ObjectMapper mapper = new ObjectMapper();
Option o = new Option();
o.id = 1;
TestOpt t = new TestOpt();
t.name = "fff";
Data data = new Data();
data.name = "data";
data.options.put("o", o);
data.options.put("t", t);
String json = mapper.writeValueAsString(data);
Data d1 = mapper.readValue(json, Data.class);
// I get error because options.get("o") contains LinkedHashMap instead of Option.class
System.out.println(((Option)d1.options.get("o")).id);
}
How can I fix this issue?
The value of the serialized json is
{"name":"data","options":{"t":{"name":"fff"},"o":{"id":1}}}
So, the problem is that the object mapper has no way to tell that the o value inside the json is an Option. The best guess is that it could be a map and thus it is deserialized as a LinkedHashMap.
If you are sure that the element o is an Option, you can convert the value using an object mapper:
Option option = mapper.convertValue(d1.options.get("o"), Option.class);
But please note, that this means that the value is again serialized and then deserialized using the right type information. You can do that, but it is not a good solution.
If it is possible, a better way would be to change your model from a generic map to a specific class that contains the type information:
class Data {
public String name;
public DataOptions options = new DataOptions();
}
class DataOptions {
public Option o;
public TestOpt t;
}
Serializing this model has the same json representation as the model using a map, and the model can be used to deserialize the json from your example.

How do I serialize a Dictionary items as flat properties of the class?

I have a class
public class Car {
public String color = null;
public String brand = null;
public HashMap<String,String> attributes;
public Car() {
this.attributes = new HashMap<>();
}
public static void main( String[] args ) {
Car car = new Car();
car.color = "Red";
car.brand = "Hyundai";
car.attributes.put("key1", "value1");
car.attributes.put("key2", "value1");
Gson gson = new Gson();
String json = gson.toJson(car);
System.out.println(json);
}
}
This currently serializes my car object into
{"color":"Red","brand":"Hyundai","attributes":{"key1":"value1","key2":"value1"}}
But I would like to unpack and serialize the attributes Dictionary from Car class as individual properties rather dictionary.
Ideally, i would like my json to be,
{"color":"Red","brand":"Hyundai","key1":"value1","key2":"value1"}
How do I achieve the same in GSON?
As explained in this Thread, there is no easy or straight forward way to achieve this.
If there is a scope of using Jackson, it can be achieved using #JsonUnwrapped but it doesn't work with Maps as explained here
but there is work around.
#JsonIgnore
public HashMap<String,String> attributes;
#JsonAnyGetter
public HashMap<String, String> getAttributes() {
return attributes;
}
And this will help create the required JSON.
(new ObjectMapper()).writeValueAsString(car)
Note. This is an alternate approach.
Use #JsonAnyGetteron the getter of your attributes

Which is better implementation? <id, <xid, object>> or <id, object>

I am trying to store 3 values. The last value is an object which can be accessed by XID. The main ID can be used to get the object.
I can think of two ways to implement this. Which would be a better approach? Also, which is better in terms of thread-safe and faster lookups?
Create a class and add it to the HashMap.
public class TestMap {
private int xid;
private XObject xobject;
public TestMap(int xid, XObject xobject) {
this.xid = xid;
this.object = object;
}
public int getXid() { return xid; }
public XObject getXOBject { return xobject; }
}
map.put(ID, new TestMap(xid, xobject));
Create a nested HashMap
HashMap<id, HashMap<xid, XObject>> map = new HashMap<>();
map.put(id, new HashMap() {{ put(xid, xobject); }} );
When you access it by the main ID, do you first need to specify the XID?
I'm assuming that you do not. In this case, I would make two Maps.
One is a Map<id,object> which you use when looking up by id. The second is a Map<xid,object> which you use when looking up by xid.

How to get one attribute from list of custom objects

Java - JxPath - Spring
I have List<MyClass> myClassList filled with MyClass objects. I am trying to find a cleanest and fastest way to get Set<String> a property out of myClassList.
class MyClass{
private String a;
private String b;
// setters getters
}
I am using jxpath for searching but I am not sure it can also do what I mentioned above.
JXPathContext ctx = JXPathContext.newContext(myClassList);
Iterate<String> aProps = ctx.iterate("? what to write");
Can you help?
instead of Jxpath there is way doing it in Guava library.
here is the implementation
public static final Function<Obj, String> FOO = new Function<Obj, String>() {
public String apply(Obj input) {
return input.foo;
}
};
List<String> fooList = Lists.transoform(targetList, Object.FOO)
or
List<String> fooList = Collections2.transoform(targetList, Object.FOO)

Need help with java map and javabean

I have a nested map:
Map<Integer, Map<Integer, Double>> areaPrices = new HashMap<Integer, Map<Integer, Double>>();
and this map is populated using the code:
while(oResult.next())
{
Integer areaCode = new Integer(oResult.getString("AREA_CODE"));
Map<Integer, Double> zonePrices = areaPrices.get(areaCode);
if(zonePrices==null)
{
zonePrices = new HashMap<Integer, Double>();
areaPrices.put(areaCode, zonePrices);
}
Integer zoneCode = new Integer(oResult.getString("ZONE_CODE"));
Double value = new Double(oResult.getString("ZONE_VALUE"));
zonePrices.put(zoneCode, value);
myBean.setZoneValues(areaPrices);
}
I want to use the value of this Map in another method of the same class. For that I have a bean.
How do I populate it on the bean, so that I can get the ZONE_VALUE in this other method
In my bean I added one new field as:
private Map<Integer, Map<Integer, Double>> zoneValues;
with getter and setter as:
public Map<Integer, Map<Integer, Double>> getZoneValues() {
return zoneValues;
}
public void setZoneValues(Map<Integer, Map<Integer, Double>> areaPrices) {
this.zoneValues = areaPrices;
}
What I am looking for to do in the other method is something like this:
Double value = myBean.get(areaCode).get(zoneCode);
How do I make it happen :(
I would like to suggest a different, hopefully more readable solution:
public class PriceMap {
private Map<Integer, Map<Integer, Double>> priceMap =
new HashMap<Integer, Map<Integer, Double>>();
// You'd use this method in your init
public Double setPrice(Integer areaCode, Integer zoneCode, Double price) {
if (!priceMap.containsKey(zoneCode)) {
priceMap.put(zoneCode, new HashMap<Integer, Double>());
}
Map<Integer, Double> areaMap = priceMap.get(zoneCode);
areaMap.put(areaCode, price);
}
public void getPrice(Integer areaCode, Integer zoneCode) {
if (!priceMap.containsKey(zoneCode)) {
// Eek! Exception or return null?
}
Map<Integer, Double> areaMap = priceMap.get(zoneCode);
return areaMap.get(areaCode);
}
}
I think this is a better, more readable abstraction which, very importantly, makes it easier for you or someone else to read after a few months.
EDIT Added get get
If you're stuck with a get(areaCode).get(zoneCode) (order reversed), but myBean is entirely yours, you could do something like:
public class MyBean {
// I suppose you have this already
private final Map<Integer, Map<Integer, Double>> priceMap =
new HashMap<Integer, Map<Integer, Double>>();
private class LooksLikeAMap implements Map<Integer, Double> {
private Integer areaCode = areaCode;
public LooksLikeAMap(Integer areaCode) {
this.areaCode = areaCode;
}
public Double get(Object zoneCode) {
if (!priceMap.containsKey(zoneCode)) {
// Eek! Exception or return null?
}
Map<Integer, Double> areaMap = priceMap.get(zoneCode);
return areaMap.get(areaCode);
}
// Implement other methods similarly
}
public Map<Integer, Double> get(Integer areaCode) {
return new LooksLikeAMap(areaCode);
}
}
OK, programming in a HTML textarea is not my strong suit, but the idea is clear.
Make some Map like structure backed by the complete data set, and initialize that
Map structure with the required AreaCode.
If the idea is not clear, post a comment fast as it's late here:)
EDIT
I am an idiot. I thought the data was zone first, then area while the get should be area first, then zone. In this case the Map already has the right structure, first area then zone, so this is not necessary. The get-get is by default if you make
public MyBean {
public Map<Integer, Double> get(Integer areaCode) {
return data.get(areaCode);
}
}
To start with, all you need is
myBean.getZoneValues(areaCode).get(zoneCode);
the while loop has an annoyance, you need to call myBean.setZoneValues(areaPrices);
out side the while loop
You can't directly control the second get() call because you have a nested Map, you'll need to return the appropriate nested Map to be able to do what you want. A getter like this should do it:
public Map<Integer, Double> get(Integer areaCode) {
return zoneValues.get(areaCode);
}
So when the client code calls get(areaCode) a map will be returned that they can then call get(zoneCode) on.
I'd suggest that you refactor to eliminate the nested Maps though, because you can't stop client code from changing the returned Map, the code is tough to read and you'll have problems if you want to add any more functionality - imagine that you want to provide a String description of an area code in future.
Something like a Map<Integer, AreaCode> where AreaCode is an object that contains what you currently have as a nested Map might be a good place to start.

Categories