将Java Bean展平为地图 [英] Flattening Java Bean to a Map

查看:126
本文介绍了将Java Bean展平为地图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我坚持将 Java Bean 转换为 Map 。互联网上有很多资源,但不幸的是,它们都将简单的bean转换为地图。我的那些更广泛。

I am stuck at converting Java Bean to Map. There are many resources on the internet, but unfortunately they all treat converting simple beans to Maps. My ones are a little bit more extensive.

有简化的例子:

public class MyBean {

  private String firstName;
  private String lastName;
  private MyHomeAddress homeAddress;
  private int age;

  // getters & setters

}

我的意思是产生 Map< String,Object> 在这种情况下,对于以下条件属实:

My point is to produce Map<String, Object> which, in this case, is true for following conditions:

map.containsKey("firstName")
map.containsKey("lastName")
map.containsKey("homeAddress.street")  // street is String
map.containsKey("homeAddress.number")  // number is int
map.containsKey("homeAddress.city")    // city is String
map.containsKey("homeAddress.zipcode") // zipcode is String
map.containsKey("age")

我尝试过使用 Apache Commons BeanUtils 。两种方法 BeanUtils #description(Object) BeanMap(Object)生成一个深层次为1的地图(我的意思是只有homeAddress键,将 MyHomeAddress 对象作为值)。我的方法应该越来越深入地进入对象,直到遇到基本类型(或字符串),然后它应该停止挖掘并插入密钥,即order.customer.contactInfo.home

I have tried using Apache Commons BeanUtils. Both approaches BeanUtils#describe(Object) and BeanMap(Object) produce a Map which "deep level" is 1 (I mean that there's only "homeAddress" key, holding MyHomeAddress object as a value). My method should enter the objects deeper and deeper until it meets a primitive type (or String), then it should stop digging and insert key i.e. "order.customer.contactInfo.home".

所以,我的问题是:它怎么可以轻松完成(或者是否已经存在允许我这样做的项目)?

So, my question is: how can it be easliy done (or is there already existing project which would allow me to do that)?

更新

我已经扩展了Radiodef的答案,还包括集合,地图数组和枚举:

I have expanded Radiodef answer to include also Collections, Maps Arrays and Enums:

private static boolean isValue(Object value) {
  final Class<?> clazz = value.getClass();
  if (value == null ||
      valueClasses.contains(clazz) ||
      Collection.class.isAssignableFrom(clazz) ||
      Map.class.isAssignableFrom(clazz) ||
      value.getClass().isArray() ||
      value.getClass().isEnum()) {
    return true;
  }
  return false;
}


推荐答案

这是一个简单的反思/递归例如。

Here's a simple reflective/recursive example.

您应该知道,按照您提出的方式进行转换存在一些问题:

You should be aware that there are some issues with doing a conversion the way you've asked:


  • 映射键必须是唯一的。

  • Java允许类将其私有字段命名为与继承类拥有的私有字段相同的名称。

这个例子没有解决这些问题,因为我不确定你想如何解释它们(如果你这样做)。如果你的bean继承了 Object 以外的东西,你需要稍微改变一下你的想法。此示例仅考虑子类的字段。

This example doesn't address those because I'm not sure how you want to account for them (if you do). If your beans inherit from something other than Object, you will need to change your idea a little bit. This example only considers the fields of the subclass.

换句话说,如果您有

public class SubBean extends Bean {

此示例仅返回<$中的字段c $ c> SubBean 。

Java让我们这样做:

Java lets us do this:

package com.acme.util;
public class Bean {
    private int value;
}

package com.acme.misc;
public class Bean extends com.acme.util.Bean {
    private int value;
}

并非任何人都应该这样做,但如果你想要这样做是个问题使用 String 作为键,因为有两个键名为value

Not that anybody should be doing that, but it's a problem if you want to use String as the keys, because there would be two keys named "value".

import java.lang.reflect.*;
import java.util.*;

public final class BeanFlattener {
    private BeanFlattener() {}

    public static Map<String, Object> deepToMap(Object bean) {
        Map<String, Object> map = new LinkedHashMap<>();
        try {
            putValues(bean, map, null);
        } catch (IllegalAccessException x) {
            throw new IllegalArgumentException(x);
        }
        return map;
    }

    private static void putValues(Object bean,
                                  Map<String, Object> map,
                                  String prefix)
            throws IllegalAccessException {
        Class<?> cls = bean.getClass();

        for (Field field : cls.getDeclaredFields()) {
            if (field.isSynthetic() || Modifier.isStatic(field.getModifiers()))
                continue;
            field.setAccessible(true);

            Object value = field.get(bean);
            String key;
            if (prefix == null) {
                key = field.getName();
            } else {
                key = prefix + "." + field.getName();
            }

            if (isValue(value)) {
                map.put(key, value);
            } else {
                putValues(value, map, key);
            }
        }
    }

    private static final Set<Class<?>> VALUE_CLASSES =
        Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            Object.class,    String.class, Boolean.class,
            Character.class, Byte.class,   Short.class,
            Integer.class,   Long.class,   Float.class,
            Double.class
            // etc.
        )));

    private static boolean isValue(Object value) {
        return value == null
            || value instanceof Enum<?>
            || VALUE_CLASSES.contains(value.getClass());
    }
}

这篇关于将Java Bean展平为地图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆