【问题标题】:Convert a JavaBean to key/value Map with nested name using commons-beans BeanUtils使用 commons-beans BeanUtils 将 JavaBean 转换为具有嵌套名称的键/值映射
【发布时间】:2011-11-17 10:49:10
【问题描述】:

我开始使用 BeanUtils 将 Properties 文件转换为 JavaBean。 使用 BeanUtils.populate,我可以很好地做到这一点。但是我可以正确地实现从 JavaBean 到 Map 的复古转换(只存储简单的值)。

根据 BeanUtils 文档中的 Employee Class 查看此示例。

import org.apache.commons.beanutils.BeanUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Employee {

    private Map<String, Address> addressMap = new HashMap<String, Address>();
    private List<Employee> subordinateList = new ArrayList<Employee>();

    private String firstName;
    private String lastName;

    public Address getAddress(String type) {
        if (!addressMap.containsKey(type)) {
            addressMap.put(type, new Address());
        }

        return addressMap.get(type);
    }

    public void setAddress(String type, Address address) {
        addressMap.put(type, address);
    }

    public Employee getSubordinate(int index) {
        if (subordinateList.size() <= index) {
            subordinateList.add(new Employee());
        }

        return subordinateList.get(index);
    }

    public void setSubordinate(int index, Employee subordinate) {
        subordinateList.add(index, subordinate);
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public class Address {
        private String city;
        private String street;
        private int number;

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getStreet() {
            return street;
        }

        public void setStreet(String street) {
            this.street = street;
        }

        public int getNumber() {
            return number;
        }

        public void setNumber(int number) {
            this.number = number;
        }
    }

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

        map.put("firstName", "MyfirstName");
        map.put("lastName", "MylastName");
        map.put("address(pro).city", "MyProCity");
        map.put("address(pro).street", "MyProStreet");
        map.put("address(pro).number", 22);

        map.put("subordinate[1].firstName", "Sub1FirstName");
        map.put("subordinate[1].lastName", "Sub1LastName");

        map.put("address(perso).city", "MyPersoCity");
        map.put("address(perso).street", "MyPersoStreet");
        map.put("address(perso).number", 2);

        map.put("subordinate[0].firstName", "Sub0FirstName");
        map.put("subordinate[0].lastName", "Sub0LastName");


        Employee employee = new Employee();
        BeanUtils.populate(employee, map);

        System.out.println(employee.getFirstName());
        System.out.println(employee.getLastName());

        System.out.println(employee.getAddress("pro").city);
        System.out.println(employee.getAddress("pro").street);
        System.out.println(employee.getAddress("pro").number);

        System.out.println(employee.getAddress("perso").city);
        System.out.println(employee.getAddress("perso").street);
        System.out.println(employee.getAddress("perso").number);

        System.out.println(employee.getSubordinate(0).firstName);
        System.out.println(employee.getSubordinate(0).lastName);

        System.out.println(employee.getSubordinate(1).firstName);
        System.out.println(employee.getSubordinate(1).lastName);

        Map<String, Object> map2 = BeanUtils.describe(employee);

        System.out.println("----------------");

        System.out.println(map2);


    }

}

结果:

MyfirstName
MylastName
MyProCity
MyProStreet
22
MyPersoCity
MyPersoStreet
2
Sub0FirstName
Sub0LastName
Sub1FirstName
Sub1LastName
----------------
{lastName=MylastName, class=class Employee, firstName=MyfirstName}

我缺少什么以便 map2 使用 BeanUtils.describe 方法实际存储诸如“address(pro).city”或“subordinate[1].firstName”之类的键?

【问题讨论】:

    标签: java apache-commons-beanutils


    【解决方案1】:

    我终于找到了解决这个问题的方法。首先,我需要根据我当前的 bean 实例检索每个嵌套的 propertyName,并且是递归的。所以我写了一个简单的方法来做到这一点:

    import org.apache.commons.beanutils.BeanUtils;
    import org.apache.commons.beanutils.PropertyUtils;
    import org.apache.commons.lang.StringUtils;
    
    import java.beans.PropertyDescriptor;
    import java.io.Serializable;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.TreeMap;
    
    public class MyPropertyUtils {
    
        public List<String> listNestedPropertyName(Object objectSource) throws Exception {
            List<String> nodeNameList = new ArrayList<String>();
    
            if (Serializable.class.isAssignableFrom(objectSource.getClass())) {
                nodeNameList.add(objectSource.toString());
                return nodeNameList;
            }
    
            PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(objectSource.getClass());
    
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
    
                Method method = propertyDescriptor.getReadMethod();
                if (propertyDescriptor.getReadMethod() == null) {
                    continue;
                }
    
                if (method.getGenericParameterTypes().length > 0) {
                    continue;
                }
    
                String name = propertyDescriptor.getName();
    
                Object value = method.invoke(objectSource);
    
                if (value == null) {
                    continue;
                }
    
                if (Map.class.isAssignableFrom(value.getClass())) { // Mapped name
                    Map map = ((Map) value);
                    name = StringUtils.substringBeforeLast(name, "Map");
    
                    for (Object key : map.keySet()) {
                        String mappedName = name + "(" + key.toString() + ")";
                        List<String> nestedNames = listNestedPropertyName(map.get(key));
    
                        for (String nestedName : nestedNames) {
                            nodeNameList.add(mappedName + "." + nestedName);
                        }
                    }
    
                } else if (List.class.isAssignableFrom(value.getClass())) { // Indexed name
                    List list = ((List) value);
                    name = StringUtils.substringBeforeLast(name, "List");
    
                    for (int i = 0; i < list.size(); i++) {
                        String indexedName = name + "[" + i + "]";
                        List<String> nestedNames = listNestedPropertyName(list.get(i));
    
                        for (String nestedName : nestedNames) {
                            nodeNameList.add(indexedName + "." + nestedName);
                        }
                    }
                } else if (Serializable.class.isAssignableFrom(value.getClass())) { // Simple Value
                    nodeNameList.add(name);
                } else { // Nested Value
                    List<String> nestedNames = listNestedPropertyName(value);
    
                    for (String nestedName : nestedNames) {
                        nodeNameList.add(name + "." + nestedName);
                    }
                }
            }
    
            return nodeNameList;
        }
    }
    

    然后我迭代其他这些名称以检索属性值,然后将它们设置在 Map 中。

    Map<String, Object> map = new HashMap<String, Object>();
    
    MyPropertyUtils myPropertyUtils = new MyPropertyUtils();
    List<String> names = myPropertyUtils.listNestedPropertyName(employee);
    for (String name : names) {
        map.put(name, PropertyUtils.getNestedProperty(employee, name));
    }
    

    这很适合我的用例。我只是在我的源对象中添加了一个访问器,用于使用常规名称(propertyName + "Map" 或 "List")访问 Map 或 List。

    也许这会让某人感兴趣。无论如何,如果有更明显的事情要做,请告诉我。

    【讨论】:

    • orika 可以将 bean 转换为地图,反之亦然。如果有人在寻找相同的东西,只是一个提示。
    • 感谢您的提示,我只是看一下该项目,它看起来很有希望(不仅考虑到这个特定问题)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多