20

If I have class like this:

class MyObject {
    public int myInt;
    public String myString;
}

Is it possible to convert instance of this class to HashMap without implementing converting code?

MyObject obj = new MyObject();
obj.myInt = 1; obj.myString = "string";
HashMap<String, Object> hs = convert(obj);

hs.getInt("myInt"); // returns 1
hs.getString("myString"); // returns "string"

Does Java provide that kind of solution, or I need to implement convert by myself?

My Class has more than 50 fields and writing converter for each field is not so good idea.

Mr.D
  • 7,353
  • 13
  • 60
  • 119
  • 1
    Hashmaps have keys and values (2 things), rather than 50 fields. Can you clarify what you are trying to achieve here? – Tim Biegeleisen May 04 '16 at 09:59
  • you´d also have to provide a custom `Map` implementation, because the valid method to get the value for a key from the `Map` is the `get(Object)` method. There is nothing like `getInt` or `getString` in order to return different types from the Map.In the end it wouldn´t make any sense to have them, because the return type is defined by the second generic type. – SomeJavaGuy May 04 '16 at 09:59
  • @TimBiegeleisen I want to convert all fields of my object to keys and values of hashmap. – Mr.D May 04 '16 at 10:02
  • @Mr.D could you eleborate a usecase for this, because i can´t see any. Calling a simple getter method would be more easy to me, especially since changing a single value in any of this fields would make the formerly generated `Map` an invalid one. – SomeJavaGuy May 04 '16 at 10:04
  • @KevinEsche there are some libs which accept only hashmaps – Mr.D May 04 '16 at 10:07
  • @KevinEsche I agree that just using getters and setters would make more sense. – Tim Biegeleisen May 04 '16 at 10:08

6 Answers6

27

With jackson library this is also possible

MyObject obj = new MyObject();
obj.myInt = 1;
obj.myString = "1";
ObjectMapper mapObject = new ObjectMapper();
Map < String, Object > mapObj = mapObject.convertValue(obj, Map.class);
Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
profcalculus
  • 281
  • 3
  • 6
  • 1
    To me, this is the quickest and less prone to error solution. Here is a reference with a more complete example using this solution https://www.mkyong.com/java/java-convert-object-to-map-example/ – hmartos Oct 08 '19 at 12:59
  • One caveat is that this solution will not keep the enum values type, it will transform them to String name /or index. – somebody Jul 26 '23 at 13:32
10

You can use reflection for implementing this behavior. You can get all fields of the class you want to convert to map iterate over this fields and take the name of each field as key of the map. This will result in a map from String to object.

Map<String, Object> myObjectAsDict = new HashMap<>();    
Field[] allFields = SomeClass.class.getDeclaredFields();
    for (Field field : allFields) {
        Class<?> targetType = field.getType();
        Object objectValue = targetType.newInstance();
        Object value = field.get(objectValue);
        myObjectAsDict.put(field.getName(), value);
    }
}
PKuhn
  • 1,338
  • 1
  • 14
  • 30
  • If some fields are Lists of objects? How to "reflect" them? – Mr.D May 04 '16 at 10:04
  • If the object relations form a tree and not a graph you can use the method described here http://stackoverflow.com/questions/6133660/recursive-beanutils-describe – PKuhn May 04 '16 at 10:08
5

Something like that will do the trick:

MyObject obj = new MyObject();
obj.myInt = 1; obj.myString = "string";
Map<String, Object> map = new HashMap<>();
// Use MyObject.class.getFields() instead of getDeclaredFields()
// If you are interested in public fields only
for (Field field : MyObject.class.getDeclaredFields()) {
    // Skip this if you intend to access to public fields only
    if (!field.isAccessible()) {
        field.setAccessible(true);
    }
    map.put(field.getName(), field.get(obj));
}
System.out.println(map);

Output:

{myString=string, myInt=1}
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
0

You might consider using a map instead of a class.

Or have your class extend a map such as

public class MyObject extends HashMap<String, Object> {

}
Mike Murphy
  • 1,006
  • 8
  • 16
0

If you don't want to use Reflection then you can use my trick. hope this may help for someone.

Suppose your class looks like this.

public class MyClass {
         private int id;
         private String name; 
}

Now Override toString() method in this class. in Eclipse there is a shortcut for generating this method also.

public class MyClass {
    private int id;
    private String name; 

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("MyClass [id=").append(id).append(", name=").append(name).append("]");
        return builder.toString();
    }

}

Now write a method inside this class that will convert your object into Map<String,String>

public Map<String, String> asMap() {
        Map<String, String> map = new HashMap<String, String>();
        String stringRepresentation = this.toString();
        if (stringRepresentation == null || stringRepresentation.trim().equals("")) {
            return map;
        }
        if (stringRepresentation.contains("[")) {
            int index = stringRepresentation.indexOf("[");
            stringRepresentation = stringRepresentation.substring(index + 1, stringRepresentation.length());
        }
        if (stringRepresentation.endsWith("]")) {
            stringRepresentation = stringRepresentation.substring(0, stringRepresentation.length() - 1);
        }
        String[] commaSeprated = stringRepresentation.split(",");
        for (int i = 0; i < commaSeprated.length; i++) {
            String keyEqualsValue = commaSeprated[i];
            keyEqualsValue = keyEqualsValue.trim();
            if (keyEqualsValue.equals("") || !keyEqualsValue.contains("=")) {
                continue;
            }
            String[] keyValue = keyEqualsValue.split("=", 2);
            if (keyValue.length > 1) {
                map.put(keyValue[0].trim(), keyValue[1].trim());
            }

        }
        return map;
    }

Now from any where in your application you can simply call this method to get your HashMap from the Object. Cheers

Abdul Mohsin
  • 1,253
  • 1
  • 13
  • 24
0

Updated approach using reflection:

  public static <T> Map<String, String> parseInnerClass(T classInstance) {
    LinkedHashMap<String, String> ret = new LinkedHashMap<>();

    for (Field attr : classInstance.getClass().getDeclaredFields()) {
      String attrValue = "";
      attr.setAccessible(true);

      try {
        attrValue = attr.get(classInstance).toString();
      } catch (IllegalAccessException | NullPointerException e) {
        // Do not add nothing
      }

      ret.put(attr.getName(), attrValue);
    }
    return ret;
  }
A.Casanova
  • 555
  • 4
  • 16