1

I have a class file for getters and setters. For another operation, I need these variable for which value are set (i.e. not null) to a HashMap.

private double Amount;
private Date abcDate;
private double Profit;
private boolean start;

public double getAmount() {
    return Amount;
}
public void setAmount(double amount) {
    Amount = amount;
}
public Date getAbcDate() {
    return abcDate;
}
public void setAbcDate(Date abcDate) {
    this.abcDate = abcDate;
}
....

I have used the ReflectionToStringBuilder for building string for another use-case.

public HashMap<String, Double> toHashMap(){
    Object myself = this;
    System.out.println(myself);
    ReflectionToStringBuilder builder = new ReflectionToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) {
    return null;
} 

Is it possible to generate a HashMap which would look something like this (The value would be what is set using the setter method)?

Key     Value
Amount  1000
abcDate 21/2/2020
Profit  200
start   true
2
  • Sorry don't understand... generate an hashmap for and from what? Commented Apr 15, 2020 at 16:03
  • @CodeScale, I've edited the question. HashMap is used for someother key-value pair operation in another function. Commented Apr 15, 2020 at 16:13

5 Answers 5

2

Do it as follows:

import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

public class Item {
    private double Amount;
    private LocalDate abcDate;
    private double Profit;
    private boolean start;

    public Item(double amount, LocalDate abcDate, double profit, boolean start) {
        Amount = amount;
        this.abcDate = abcDate;
        Profit = profit;
        this.start = start;
    }

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE, true, true);
    }

    public Map<String, String> toHashMap() {
        Map<String, String> map = new LinkedHashMap<String, String>();
        String str = toString();
        str = str.substring(str.indexOf("[") + 2).replace("]", "");
        String[] tokens = str.split("\n");
        for (String token : tokens) {
            String[] fv = token.trim().split("=");
            if (!fv[1].equals("<null>")) {
                map.put(fv[0], fv[1]);
            }
        }
        return map;
    }

    public static void main(String[] args) {
        Item item1 = new Item(1000, LocalDate.of(2020, 2, 21), 200, true);
        System.out.println(item1.toHashMap());

        Item item2 = new Item(1000, null, 200, true);
        System.out.println(item2.toHashMap());
    }
}

Output:

{Amount=1000.0, abcDate=2020-02-21, Profit=200.0, start=true}
{Amount=1000.0, Profit=200.0, start=true}

You have mentioned that you are already familiar with ReflectionToStringBuilder. I hope, you are also familiar with String::split. Rest of the logic is straight forward.

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, correct. Based on your answer, instead of ToStringStyle.MULTI_LINE_STYLE if you use the ToStringStyle.JSON_STYLE, you may directly push it to a JSON object.
@Betafish - You are most welcome. Please do not forget to accept the answers so that future visitors can also use the solution confidently.
1

You can achieve that through reflection :

    Field[] fields = this.getClass().getFields();

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

     for(Field f : fields)
            map.put(f.getName(),f.get(this).toString());

1 Comment

thats what i have done
1

If my understanding is correct, the following executable class does what you expect.

public class Reflection {

public static void main(String[] args) throws Exception {

    var bean = new Bean();
    bean.setAmount(1000);
    bean.setAbcDate(new Date());
    bean.setProfit(200);
    bean.setStart(true);

    var result = new HashMap<String, Object>();
    for (var f : Bean.class.getDeclaredFields()) {
        result.put(f.getName(), f.get(bean));
    }

    System.out.println(result);
}

public static class Bean {
    private double Amount;
    private Date abcDate;
    private double Profit;
    private boolean start;

    public void setAmount(double amount) {
        Amount = amount;
    }

    public void setAbcDate(Date abcDate) {
        this.abcDate = abcDate;
    }

    public void setProfit(double profit) {
        Profit = profit;
    }

    public void setStart(boolean start) {
        this.start = start;
    }
}

}

Comments

1

The correct way of doing this is to utilize the Introspector. Here's a Java 8+ way to do that:

public static Map<String, Object> beanPropertyMap(final Object instance) {
    requireNonNull(instance, "instance may not be null");
    try {
        return Arrays.stream(
            Introspector.getBeanInfo(instance.getClass(), Object.class) 
                        // introspect the class hierarchy below Object.class
                        .getPropertyDescriptors())
                     .map(pd -> invokeGetter(pd, instance))
                     .filter(t -> t.getValue() != null)
                     .collect(Collectors.toMap(Tuple::getKey, Tuple::getValue));
    } catch (IntrospectionException e) {
        throw new IllegalStateException(e);
    }
}

private static Tuple invokeGetter(final PropertyDescriptor propertyDescriptor, final Object instance) {
    String key = propertyDescriptor.getName();
    Object value;
    try {
        value = propertyDescriptor.getReadMethod().invoke(instance);
    } catch (IllegalAccessException | InvocationTargetException e) {
        // you may want to log something here
        value = null;
    }
    return new Tuple(key, value);
}

private static class Tuple {

    private final String key;
    private final Object value;

    Tuple(final String key, final Object value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() {
        return key;
    }

    public Object getValue() {
        return value;
    }

}

The difference between this and the other proposed solutions is that according to the Javabeans specification, properties are defined not by fields, but by accessors (getters and / or setters). The accessors of a given property "foo" are captured in a PropertyDescriptor with the name "foo", which references the getter (if present) through pd.getReadMethod() and the setter (if present) through pd.getWriteMethod(). It is completely valid for a Javabeans property to be

  1. read-only (getter only)
  2. write-only (setter only)
  3. read-write (both)

By convention, a Javabeans property should be backed up by a field with the same name as the property name, but that's just a convention. Javabeans can be backed by a different field, a different method, or no field at all.

The other difference is that if your class has superclasses, this method will also return their bean properties, up to (but not including) Object.class.

Comments

0

If i understood what you mean, your solution should be the following:

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ReflectionToMap {
    private static void toMap(Object obj, Map<String, Object> map, boolean includeSuperclass) {
        Class<?> cls = obj.getClass();
        try {

            if (cls.isArray()) {
                for (int i = 0; i < Array.getLength(obj); i++)
                    map.put("" + i, Array.get(obj, i));
            } else {
                while (cls != null && !cls.isInterface()) {
                    Field[] fields = cls.getDeclaredFields();
                    for (Field field : fields) {
                        if (Modifier.isStatic(field.getModifiers()))
                            continue;
                        boolean accessible = field.isAccessible();
                        field.setAccessible(true);
                        Object value = field.get(obj);
                        field.setAccessible(accessible);
                        if (includeSuperclass)
                            map.put(cls.getCanonicalName() + "." + field.getName(), value);
                        else
                            map.put(field.getName(), value);
                    }
                    if (includeSuperclass)
                        cls = cls.getSuperclass();
                    else
                        return;
                }
            }
        } catch (Exception e) {
            System.err.println("Something gone wrong...");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");

        Map<String, Object> map = new HashMap<>();
        toMap(list, map, true);
        System.out.println(map);

        map.clear();
        toMap(list, map, false);
        System.out.println(map);
    }
}

The method toMap() takes an object to convert to map and the map that will contains the result. The method includes fields from superclasses, too. Each field name, includes the canonical name of the class/abstract class, which it belongs to. The included main method outputs the following:

{java.util.ArrayList.elementData=[Ljava.lang.Object;@70dea4e, java.util.ArrayList.size=2, java.util.AbstractList.modCount=2}
{size=2, elementData=[Ljava.lang.Object;@70dea4e}

The catched exception cannot be thrown, because the access to fields is safe.

You can use the toMap() method in the toMap() method of your class, too.

As you see, this method accepts a boolean to inhibit the inclusion of fields from superclasses. In this case it does not include the canonical name of the class in map keys.

See that if obj is array, the map will be something like:

1 -> obj1
2 -> obj2
3 -> obj3
...and so on...

1 Comment

good suggestion. need to check with types like ISODate (for mongo)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.