3

How to create a list of maps, where each key name is inferred from name of the class attribute, and value is to be put by getter method

I am having following class in java

class DTA {
  private String id;
  private String age;

  @Override
  public String toString() {
    return "DTA{" +
            "id='" + id + '\'' +
            ", age='" + age + '\'' +
            '}';
  }

  public DTA(String id, String age) {
    this.id = id;
    this.age = age;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getAge() {
    return age;
  }

  public void setAge(String age) {
    this.age = age;
  }
}

I am having a list of objects of type DTA

List<DTA> listA = new ArrayList<>();
listA.add(new DTA("A", "15"));
listA.add(new DTA("B", "25"));

I want to create an ordered list of maps (somewhat like scala) which has following content.

List<? extends Map<String, String>>

List(Map("id"->"A", "age"->"15"), Map("id"->"B", "age"->"25"))
GhostCat
  • 137,827
  • 25
  • 176
  • 248
axnet
  • 5,146
  • 3
  • 25
  • 45

2 Answers2

4

Without "dynamics", the straight forward thing might look like:

List<Map<String, String>> x = listA
            .stream()
            .map(this::toMap)
            .collect(Collectors.toList());

with a local helper, such as:

private Map<String, String> toMap(DTA dta) {
    Map<String, String> rv = new HashMap<>();
    rv.put("id", dta.getId());
    rv.put("age", dta.getAge());
    return rv;
}

In order to be fully dynamic here, you would have to use reflection to query the field names. You can find examples how to do that here.

But I strongly suggest to not do that: reflection should always be your last resort. The notion of DTA suggests that you have that object data coming from some "service" anyway. If so, why first serialize into a specific DTA class, to then "flatten" that information into some generic Map structure?!

Meaning: when that service gives you objects that are serialized as, say JSON, or XML ... then it would be much better to simply use a library like gson or jackson to directly deserialize that data into such generic "flat" Map-based objects. Jackson for example has a JsonNode class. When you deserialize into such objects, you get that mapping of field names for free! See here more example code.

The point is: identifying fields using reflection is possible. But reflection code is always tedious, and error prone. If possible, stay away from doing that yourself.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
3

Basically, the tool used to "look into" the contents of classes in Java is called reflection. For example, if your object is a POJO (Plain Old Java Object), you can iterate over all fields in the class in the following way:

DTA obj; // some object, I assume it's initialized
Field[] fields = DTA.class.getDeclaredFields();
Map<String, Object> valuesMap = new HashMap<>();
for (field : fields) {
    boolean wasAccessible = field.isAccessible(); // check if class has access control disabled
    field.setAccessible(true); // disable access control (private/protected) to extract value    
    valuesMap.put(field.getName(), field.get(obj));
    field.setAccessible(wasAccessible); // return flag to initial value   
}

However, accessing values via reflection this way is notoriously hacky. Unless you have good reasons to do it yourself, try using a framework that automates tasks like that rather than writing code like this from scratch.

Also, reflection is slow. Accessing Field entities like that for every single object is suboptimal, if you ever want to really write code like this, you should cache the Field objects in a Map<String, Field> and only do the setAccessible override and the Field retrieval once for every collection of DTA objects.

Piotr Wilkin
  • 3,446
  • 10
  • 18
  • A nice list of reasons why you should not use reflection, but together with an example how to do it. Good answer, worth an upvote! – GhostCat Jun 18 '19 at 17:15