Object in Object to HashMap with reflection

Filipe :

Project:

I am currently using a tool called Netuno to develop an api, so to do the export of objects to json it is necessary to do the transformation of the object to HashMap, for this a method was developed in a parent class to export the object.

The method:
   public Map<String, Object> export() {
        Object obj = this;
        Map<String, Object> map = new HashMap<>();
        for (Field field : obj.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(obj));
            } catch (Exception e) {
                //todo e
            }
        }
        return map;
    }

This works, but only for simple objects.

My problem:

If the object has complex objects inside my method it has no ability to export them to HashMap either.

Example structure:

public abstract class Master {
 public Map < String, Object >
  export () {
   Object obj = this;
   Map < String, Object > map = new HashMap < > ();
   for (Field field: obj.getClass().getDeclaredFields()) {
    field.setAccessible(true);
    try {
     map.put(field.getName(), field.get(obj));
    } catch (Exception e) {
     //todo e
    }
   }
   return map;
  }
}

public class Foo extends Master {
 private int a;
 private int b;
 private String c;
 private Bar bar;
//...
}

public class Bar extends Master {
 private int q;
 private int w;
 private String e;
//...
}

I use it this way:

return new Bar(/*data*/).export();

Output:

{
  "a": 2,
  "b": 5,
  "c": "abc",
  "bar": "myproject.myPackage.Bar@XXXXX"
}

Expected Output:

{
  "a": 2,
  "b": 5,
  "c": "abc",
  "bar": {
    "q": 10,
    "w": 15,
    "e": "it works"
  }
}
Samuel Philipp :

You need to decide if a value should me put plain in the map or recursively. Therefore you can use a simple method like this:

private boolean isSimpleType(Class<?> type) {
    return type.isPrimitive() ||
            Boolean.class == type ||
            Character.class == type ||
            CharSequence.class.isAssignableFrom(type) ||
            Number.class.isAssignableFrom(type) ||
            Enum.class.isAssignableFrom(type);
}

This method returns true for all primitive types or the Wrapper objects, Strings and Enums. You can simply adjust that methods to fit your needs. Look here for more details on how to determine if a class is simple.

Now you can use to transform an object:

public Map<String, Object> convert(Object object) {
    Map<String, Object> map = new HashMap<>();
    for (Field field : object.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        try {
            if (isSimpleType(field.getType())) {
                map.put(field.getName(), field.get(object));
            } else {
                map.put(field.getName(), convert(field.get(object)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return map;
}

You also can adjust this to do fir your export method.

The result for your example will be this:

{a=2, b=5, bar={q=10, e=it works, w=15}, c=abc}

Beside that I would strongly recommend using an library (like Jackson), which already does stuff like this by perfection. Here is the same solution using Jackson:

Map result = new ObjectMapper().convertValue(object, Map.class);

Or if you need type safety:

ObjectMapper mapper = new ObjectMapper();
MapType mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
Map<String, Object> result = mapper.convertValue(value, mapType);

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=156777&siteId=1
Recommended