Sunday 26 May 2024

Populating a Java Bean from a Map Using Introspection

In this post, I am going to explain how to populate a Java bean from a map.

Input

Map<String, Object> map = new HashMap<>();

map.put("id", 123);
map.put("name", "Krishna Gurram");
map.put("age", 34);

We need to write an utility method, that takes a Map and a bean class as argument, and return the bean by populating the properties from the map.

 

public static <T> T fromMap(Map<String, Object> map, Class<T> clazz) throws Exception {

}

 

Simple solution looks like below.

Get all property descriptors of the class.

BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

For each property of the class, find the setter method of this property and set the value.

for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  String name = propertyDescriptor.getName();
  Object value = map.get(name);
  Method setterMethod = propertyDescriptor.getWriteMethod();
  setterMethod.invoke(object, value);
}

 

Apart from this, we need to handle other scenarios like

a.   Whether the value is compatible with given value.

b.   Whether is there any setter method for the given variable etc.,

 

Find the below working application.

 

Employee.java

 

package com.sample.app.model;

public class Employee {
  private static int totalEmployees;

  private Integer id;
  private String name;
  private int age;

  public Integer getId() {
    return id;
  }

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

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

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

  public static int getTotalemployees() {
    return totalEmployees;
  }

  @Override
  public String toString() {
    return "Employee [id=" + id + ", name=" + name + ", age=" + age + "]";
  }

}

PopulateJavaObjectFromAMap.java

package com.sample.app.introspector;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.sample.app.model.Employee;

public class PopulateJavaObjectFromAMap {

  private static boolean matchesPrimitive(final Class<?> targetType, final Class<?> valueType) {
    if (!targetType.isPrimitive()) {
      return false;
    }

    try {
      // see if there is a "TYPE" field. This is present for primitive wrappers.
      final Field typeField = valueType.getField("TYPE");
      final Object primitiveValueType = typeField.get(valueType);

      if (targetType == primitiveValueType) {
        return true;
      }
    } catch (final NoSuchFieldException | IllegalAccessException ignored) {
      // If the TYPE field is inaccessible, it suggests that we're not dealing with a
      // primitive wrapper.
      // There's no action required in this case, as we can't check for compatibility.
    }
    return false;
  }

  private static boolean isCompatibleType(final Object value, final Class<?> type) {
    return value == null || type.isInstance(value) || matchesPrimitive(type, value.getClass());
  }

  public static <T> T fromMap(Map<String, Object> map, Class<T> clazz) throws Exception {
    BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
    PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

    T object = clazz.getDeclaredConstructor().newInstance();

    for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {

      try {
        String name = propertyDescriptor.getName();
        Object value = map.get(name);

        Method setterMethod = propertyDescriptor.getWriteMethod();
        if (setterMethod == null) {
          continue;
        }
        final Class<?> firstParameterType = setterMethod.getParameterTypes()[0];

        if (!isCompatibleType(value, firstParameterType)) {
          throw new Exception("Can't set the value " + value + " to the " + name);
        }
        setterMethod.invoke(object, value);

      } catch (Exception e) {
        throw e;
      }

    }

    return object;

  }

  public static void main(String[] args) throws Exception {
    Map<String, Object> map = new HashMap<>();

    map.put("id", 123);
    map.put("name", "Krishna Gurram");
    map.put("age", 34);

    Employee emp = fromMap(map, Employee.class);
    System.out.println(emp);

  }

}

Output

Employee [id=123, name=Krishna Gurram, age=34]

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment