在Java开发中,我们经常会遇到需要将复杂的嵌套对象转换为扁平键值对映射的场景,比如将实体类对象转换为适合存储到Redis的Map结构,或者将配置类的嵌套属性转换为统一的配置键值对。递归扁平化可以自动处理对象的多层嵌套结构,无需手动逐层解析属性,大幅提升开发效率。

核心实现思路
要实现Java对象的递归扁平化,首先需要明确处理的对象类型和转换规则:
- 对于基础类型、包装类型、字符串等简单属性,直接将其属性名作为键,属性值作为值存入映射
- 对于自定义对象类型的属性,需要递归处理该对象的属性,键名拼接当前属性名和子属性名,用分隔符连接
- 对于集合、数组类型的属性,需要遍历每个元素,键名拼接当前属性名和元素索引,再递归处理元素内容
- 对于Map类型的属性,需要遍历每个键值对,键名拼接当前属性名和Map的键,再递归处理Map的值
基础工具类实现
下面是一个通用的递归扁平化工具类,支持处理上述多种属性类型,默认使用点号作为键名分隔符:
import java.lang.reflect.Field;
import java.util.*;
public class ObjectFlattener {
private static final String DEFAULT_SEPARATOR = ".";
/**
* 将对象递归扁平化为键值对映射
* @param obj 待转换的对象
* @return 扁平化后的Map
*/
public static Map<String, Object> flatten(Object obj) {
Map<String, Object> result = new HashMap<>();
if (obj == null) {
return result;
}
// 处理基础类型、包装类型、字符串等简单类型
if (isSimpleType(obj.getClass())) {
result.put("", obj);
return result;
}
// 处理Map类型
if (obj instanceof Map) {
flattenMap((Map<?, ?>) obj, "", result, DEFAULT_SEPARATOR);
return result;
}
// 处理数组类型
if (obj.getClass().isArray()) {
flattenArray(obj, "", result, DEFAULT_SEPARATOR);
return result;
}
// 处理集合类型
if (obj instanceof Collection) {
flattenCollection((Collection<?>) obj, "", result, DEFAULT_SEPARATOR);
return result;
}
// 处理自定义对象类型
flattenObject(obj, "", result, DEFAULT_SEPARATOR);
return result;
}
/**
* 判断是否为简单类型
*/
private static boolean isSimpleType(Class<?> clazz) {
return clazz.isPrimitive()
|| clazz.equals(String.class)
|| clazz.equals(Integer.class)
|| clazz.equals(Long.class)
|| clazz.equals(Double.class)
|| clazz.equals(Float.class)
|| clazz.equals(Boolean.class)
|| clazz.equals(Character.class)
|| clazz.equals(Byte.class)
|| clazz.equals(Short.class)
|| clazz.equals(Date.class);
}
/**
* 递归处理自定义对象
*/
private static void flattenObject(Object obj, String prefix, Map<String, Object> result, String separator) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object value = field.get(obj);
String key = prefix.isEmpty() ? field.getName() : prefix + separator + field.getName();
processValue(value, key, result, separator);
} catch (IllegalAccessException e) {
// 忽略无法访问的属性
}
}
}
/**
* 递归处理Map类型
*/
private static void flattenMap(Map<?, ?> map, String prefix, Map<String, Object> result, String separator) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
String key = prefix.isEmpty() ? entry.getKey().toString() : prefix + separator + entry.getKey().toString();
processValue(entry.getValue(), key, result, separator);
}
}
/**
* 递归处理数组类型
*/
private static void flattenArray(Object array, String prefix, Map<String, Object> result, String separator) {
int length = java.lang.reflect.Array.getLength(array);
for (int i = 0; i < length; i++) {
Object value = java.lang.reflect.Array.get(array, i);
String key = prefix + separator + i;
processValue(value, key, result, separator);
}
}
/**
* 递归处理集合类型
*/
private static void flattenCollection(Collection<?> collection, String prefix, Map<String, Object> result, String separator) {
int index = 0;
for (Object item : collection) {
String key = prefix + separator + index;
processValue(item, key, result, separator);
index++;
}
}
/**
* 处理属性值,根据类型选择不同的处理逻辑
*/
private static void processValue(Object value, String key, Map<String, Object> result, String separator) {
if (value == null) {
result.put(key, null);
return;
}
Class<?> clazz = value.getClass();
if (isSimpleType(clazz)) {
result.put(key, value);
} else if (value instanceof Map) {
flattenMap((Map<?, ?>) value, key, result, separator);
} else if (clazz.isArray()) {
flattenArray(value, key, result, separator);
} else if (value instanceof Collection) {
flattenCollection((Collection<?>) value, key, result, separator);
} else {
flattenObject(value, key, result, separator);
}
}
}
使用示例
假设我们有如下嵌套的实体类结构:
class User {
private String name;
private Integer age;
private Address address;
private List<String> hobbies;
private Map<String, String> extraInfo;
// 省略构造方法、getter、setter
}
class Address {
private String province;
private String city;
private String street;
// 省略构造方法、getter、setter
}
创建对象并调用扁平化工具类:
public class Test {
public static void main(String[] args) {
User user = new User();
user.setName("张三");
user.setAge(25);
Address address = new Address();
address.setProvince("广东");
address.setCity("深圳");
address.setStreet("科技园路");
user.setAddress(address);
List<String> hobbies = new ArrayList<>();
hobbies.add("篮球");
hobbies.add("阅读");
user.setHobbies(hobbies);
Map<String, String> extraInfo = new HashMap<>();
extraInfo.put("job", "程序员");
extraInfo.put("level", "中级");
user.setExtraInfo(extraInfo);
Map<String, Object> flatMap = ObjectFlattener.flatten(user);
for (Map.Entry<String, Object> entry : flatMap.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
输出结果如下:
name : 张三 age : 25 address.province : 广东 address.city : 深圳 address.street : 科技园路 hobbies.0 : 篮球 hobbies.1 : 阅读 extraInfo.job : 程序员 extraInfo.level : 中级
注意事项
- 上述实现使用反射获取对象属性,无法处理父类的属性,如果需要支持父类属性,可以修改获取字段的逻辑,遍历所有父类的字段
- 如果对象中存在循环引用,递归处理会导致栈溢出,需要根据实际需求添加循环引用检测逻辑
- 默认分隔符是点号,如果需要使用其他分隔符,可以扩展工具类的方法,支持自定义分隔符参数
- 对于静态属性、final属性等特殊情况,上述代码没有做特殊处理,实际使用时可以根据需求调整反射获取字段的逻辑