最近遇到一个需求,就是给出一个Object
列表,Object
类型只在A、B、C、D、E、F、G
这七个类中,这七个类各自有着不同的属性,甲方要求根据这个列表生成一段文字,来描述这个列表中各个对象的信息,同一个类的信息格式一样就可以。比如:
1.水果名称:苹果 类型:红富士 来源:烟台 //水果时
2.蔬菜名称:白菜 类型:大叶类 保鲜期:5天
3.蔬菜名称:大葱 类型:章丘大葱 保鲜期:7天
4.商品名称:紫米面包 保质期:6天
5.工具名称:电子称 功能:拍照识别商品 成本:300块
以上数据是乱写的,但类似这种,每个类有自己的模板,同一个类格式一致,为了应对以后甲方可能会修改格式的需求,要求格式可以配置,于是找到了一个工具类,使用示例如下
import java.util.*;
public class Demo01 {
public static void main(String[] args) {
Person mother = new Person();
mother.name = "母亲";
mother.age = 40;
Person son = new Person();
son.name = "小明";
son.age = 12;
Person grandpa = new Person();
grandpa.name = "爷爷";
grandpa.age = 61;
mother.parent = grandpa;
mother.children = son;
// 字符串模板可以存到数据库中
String template = "一家之主 ${age} 岁的 ${name} 的父母 ${parent.name} 已经 ${parent.age} 岁了,其后代 ${children.name} 也 ${children.age} 岁了";
// 这个就是工具类了
PlaceholderResolver defaultResolver = PlaceholderResolver.getDefaultResolver();
// 调用该方法进行解析,就可以得到需要的结果了
String result = defaultResolver.resolveByObject(template, mother);
System.out.println(result);
}
}
class Person{
String name;
Integer age;
Person parent;
Person children;
}
以下是该工具类对应的源码:
PlaceholderResolver
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import org.jetbrains.annotations.NotNull;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* 占位符解析器
*
* @author meilin.huang
* @version 1.0
* @date 2018-11-13 1:42 PM
*/
public class PlaceholderResolver {
/**
* 默认前缀占位符
*/
private static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/**
* 默认后缀占位符
*/
private static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/**
* 默认单例解析器
*/
private static PlaceholderResolver defaultResolver = new PlaceholderResolver();
/**
* 占位符前缀
*/
private String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
/**
* 占位符后缀
*/
private String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
private PlaceholderResolver() {
}
private PlaceholderResolver(String placeholderPrefix, String placeholderSuffix) {
this.placeholderPrefix = placeholderPrefix;
this.placeholderSuffix = placeholderSuffix;
}
/**
* 获取默认的占位符解析器,即占位符前缀为"${", 后缀为"}"
*
* @return
*/
public static PlaceholderResolver getDefaultResolver() {
return defaultResolver;
}
public static PlaceholderResolver getResolver(String placeholderPrefix, String placeholderSuffix) {
return new PlaceholderResolver(placeholderPrefix, placeholderSuffix);
}
/**
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}
* 如:template = category:${}:product:${}
* values = {"1", "2"}
* 返回 category:1:product:2
*
* @param content 要解析的带有占位符的模板字符串
* @param values 按照模板占位符索引位置设置对应的值
* @return
*/
public String resolve(String content, String[] values) {
int start = content.indexOf(this.placeholderPrefix);
if (start == -1) {
return content;
}
//值索引
int valueIndex = 0;
StringBuilder result = new StringBuilder(content);
while (start != -1) {
int end = result.indexOf(this.placeholderSuffix);
String replaceContent = values[valueIndex++];
result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
}
return result.toString();
}
/**
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}
* 如:template = category:${}:product:${}
* values = {"1", "2"}
* 返回 category:1:product:2
*
* @param content 要解析的带有占位符的模板字符串
* @param values 按照模板占位符索引位置设置对应的值
* @return
*/
public String resolve(String content, Object... values) {
return resolve(content, Stream.of(values).map(String::valueOf).toArray(String[]::new));
}
/**
* 根据替换规则来替换指定模板中的占位符值
*
* @param content 要解析的字符串
* @param rule 解析规则回调
* @return
*/
public String resolveByRule(String content, Function<String, String> rule) {
int start = content.indexOf(this.placeholderPrefix);
if (start == -1) {
return content;
}
StringBuilder result = new StringBuilder(content);
while (start != -1) {
int end = result.indexOf(this.placeholderSuffix, start);
//获取占位符属性值,如${id}, 即获取id
String placeholder = result.substring(start + this.placeholderPrefix.length(), end);
//替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
String replaceContent = placeholder.trim().isEmpty() ? "" : rule.apply(placeholder);
result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
}
return result.toString();
}
/**
* 替换模板中占位符内容,占位符的内容即为map key对应的值,key为占位符中的内容。
* 如:content = product:${id}:detail:${did}
* valueMap = id -> 1; pid -> 2
* 经过解析返回 product:1:detail:2
*
* @param content 模板内容。
* @param valueMap 值映射
* @return 替换完成后的字符串。
*/
public String resolveByMap(String content, final Map<String, Object> valueMap) {
return resolveByRule(content, placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
}
/**
* 根据properties文件替换占位符内容
*
* @param content
* @param properties
* @return
*/
public String resolveByProperties(String content, final Properties properties) {
return resolveByRule(content, properties::getProperty);
}
/**
* 根据对象中字段路径(即类似js访问对象属性值)替换模板中的占位符
* 如 content = product:${id}:detail:${detail.id}
* obj = Product.builder().id(1).detail(Detail.builder().id(2).build()).build();
* 经过解析返回 product:1:detail:2
*
* @param content 要解析的内容
* @param obj 填充解析内容的对象(如果是基本类型,则所有占位符替换为相同的值)
* @return
*/
@SuppressWarnings("unchecked")
public String resolveByObject(String content, final Object obj) {
if (obj instanceof Map) {
return resolveByMap(content, (Map<String, Object>) obj);
}
ReflectUtil.getFieldValue("","");
return resolveByRule(content, placeholderValue -> String.valueOf(ReflectionUtils.getValueByFieldPath(obj, placeholderValue)));
}
/**
* 格式化字符串,可以使用类似${obj.name}的形式来取map里面的值,如果没有取到对应的值,则不做任何修改, 这个方法是我自己写的,其他的方法都是大佬写的
* 如:str="姓名:${name},出生年月:${year}",map={name="张三丰",year=1990},则format(str,map) = "姓名:张三丰,出生年月:1990"
* @param str 包含${obj.value}的字符串
* @param map 包含所有对象属性的map
* @return
*/
@NotNull
public static String format(String str, Map<String, Object> map) {
StringBuilder sb = new StringBuilder(str);
Pattern p = Pattern.compile("\\$\\{\\w+\\}");
Matcher matcher = p.matcher(str);
while (matcher.find()) {
String param = matcher.group();
String key = param.substring(param.indexOf("{")+1,param.lastIndexOf("}"));
if (map.containsKey(key)) {
int start = sb.indexOf(param);
sb.replace(start,start + param.length(), StrUtil.toString(map.get(key)));
}
}
return sb.toString();
}
/**
* 将List
public static List<Map<String, Object>> convertUnderlineToCamelCaseObject(Object obj) {
List<Map<String, Object>> rList = new ArrayList<>();
if (obj instanceof List) {
List list = (List) obj;
for (Object o : list) {
if (o instanceof Map) {
Map m = (Map) o;
Map<String, Object> rMap = new HashMap<>();
Set<Map.Entry> set = m.entrySet();
for (Map.Entry entry : set) {
String key = StrUtil.toString(entry.getKey());
if (key != null && key.indexOf("_") > -1) {
String nkey = underlineToCamelCase(key);
rMap.put(nkey, entry.getValue());
} else {
rMap.put(key, entry.getValue());
}
}
rList.add(rMap);
}
}
}
return rList;
}
/**
* 将下划线形式转成大小写的形式
* @param text
* @return
*/
public static String underlineToCamelCase(String text) {
String[] words = text.split("[\\W_]+");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < words.length; i++) {
String word = words[i];
if (i == 0) {
word = word.isEmpty() ? word : word.toLowerCase();
} else {
word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase();
}
builder.append(word);
}
return builder.toString();
}
/**
* 将 驼峰形式的字符串 转成 全小写/大写以下划线分割形式的字符串
* @param text
* @param defaultConfig true,全转成大写; 默认小写
* @return
*/
public static String camelCaseToUnderline(String text,Boolean ...defaultConfig){
Matcher matcher = Pattern.compile("[A-Z]").matcher(text);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, "_" + matcher.group().toLowerCase());
}
matcher.appendTail(sb);
if (sb.toString().startsWith("_")) {
sb.delete(0, 1);
}
if (defaultConfig.length>0 && defaultConfig[0]) {
return sb.toString().toUpperCase();
}
return sb.toString();
}
/**
* 把long型的秒数转成localDateTime
* @param seconds
* @return
*/
public static LocalDateTime convertLongSecondsToLocalDateTime(Long seconds) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(seconds*1000), ZoneId.systemDefault());
}
public static void main(String[] args) {
ArrayList<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("my_name", "张三");
map.put("my_age",18);
list.add(map);
System.out.println(convertUnderlineToCamelCaseObject(list));
}
static class Product {
private String name;
private String code;
}
static class User{
private Integer id;
private Product product;
}
}
ObjectUtils
import java.time.temporal.TemporalAccessor;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
/**
* @author meilin.huang
* @version 1.0
* @date 2019-04-18 09:47
*/
public class ObjectUtils {
/**
* 如果obj为null,则返回默认值,不为null,则返回obj
*
* @param obj obj
* @param defaultValue 默认值
* @param 值泛型
* @return obj不为null 返回obj,否则返回默认值
*/
public static <T> T defaultIfNull(T obj, T defaultValue) {
return obj != null ? obj : defaultValue;
}
//---------------------------------------------------------------------
// 对象类型判断
//---------------------------------------------------------------------
public static boolean isCollection(Object obj) {
return obj instanceof Collection;
}
public static boolean isMap(Object obj) {
return obj instanceof Map;
}
public static boolean isNumber(Object obj) {
return obj instanceof Number;
}
public static boolean isBoolean(Object obj) {
return obj instanceof Boolean;
}
public static boolean isEnum(Object obj) {
return obj instanceof Enum;
}
public static boolean isDate(Object obj) {
return obj instanceof Date || obj instanceof TemporalAccessor;
}
public static boolean isCharSequence(Object obj) {
return obj instanceof CharSequence;
}
/**
* 判断对象是否为八大基本类型包装类除外即(boolean, byte, char, short, int, long, float, and double)
*
* @param obj
* @return
*/
public static boolean isPrimitive(Object obj) {
return obj != null && obj.getClass().isPrimitive();
}
/**
* 判断对象是否为包装类或者非包装类的基本类型
*
* @param obj
* @return
*/
public static boolean isWrapperOrPrimitive(Object obj) {
return isPrimitive(obj) || isNumber(obj) || isCharSequence(obj) || isBoolean(obj);
}
/**
* 判断一个对象是否为数组
*
* @param obj
* @return
*/
public static boolean isArray(Object obj) {
return obj != null && obj.getClass().isArray();
}
/**
* 判断一个对象是否为基本类型数组即(int[], long[], boolean[], double[]....)
*
* @param obj
* @return
*/
public static boolean isPrimitiveArray(Object obj) {
return isArray(obj) && obj.getClass().getComponentType().isPrimitive();
}
}
ReflectionUtils
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @author meilin.huang
* @version 1.0
* @date 2019-03-27 10:04 AM
*/
public final class ReflectionUtils {
/**
* 获取所有field字段,包含父类继承的
*
* @param clazz 字段所属类型
* @return
*/
public static Field[] getFields(Class<?> clazz) {
return getFields(clazz, null);
}
/**
* 获取指定类的所有的field,包括父类
*
* @param clazz 字段所属类型
* @param fieldFilter 字段过滤器
* @return 符合过滤器条件的字段数组
*/
public static Field[] getFields(Class<?> clazz, Predicate<Field> fieldFilter) {
List<Field> fields = new ArrayList<>(32);
while (Object.class != clazz && clazz != null) {
// 获得该类所有声明的字段,即包括public、private和protected,但是不包括父类的申明字段,
// getFields:获得某个类的所有的公共(public)的字段,包括父类中的字段
for (Field field : clazz.getDeclaredFields()) {
if (fieldFilter != null && !fieldFilter.test(field)) {
continue;
}
fields.add(field);
}
clazz = clazz.getSuperclass();
}
return fields.toArray(new Field[0]);
}
/**
* 对指定类的所有字段执行consumer操作
*
* @param clazz 目标对象
* @param consumer 对字段进行操作
*/
public static void doWithFields(Class<?> clazz, Consumer<Field> consumer) {
Arrays.stream(getFields(clazz)).forEach(consumer);
}
/**
* 获取指定类的指定field,包括父类
*
* @param clazz 字段所属类型
* @param name 字段名
* @return
*/
public static Field getField(Class<?> clazz, String name) {
return getField(clazz, name, null);
}
/**
* 获取指定类的指定field,包括父类
*
* @param clazz 字段所属类型
* @param name 字段名
* @param type field类型
* @return Field对象
*/
public static Field getField(Class<?> clazz, String name, Class<?> type) {
Assert.notNull(clazz, "clazz不能为空!");
while (clazz != Object.class && clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if ((name == null || name.equals(field.getName())) &&
(type == null || type.equals(field.getType()))) {
return field;
}
}
clazz = clazz.getSuperclass();
}
return null;
}
/**
* 获取字段值
*
* @param field 字段
* @param target 字段所属实例对象
* @return 字段值
*/
public static Object getFieldValue(Field field, Object target) {
makeAccessible(field);
try {
return field.get(target);
} catch (Exception e) {
throw new IllegalStateException(String.format("获取%s对象的%s字段值错误!"
, target.getClass().getName(), field.getName()), e);
}
}
/**
* 获取对象中指定field值
*
* @param obj 对象
* @param fieldName 字段名
* @return 字段值
*/
public static Object getFieldValue(Object obj, String fieldName) {
Assert.notNull(obj, "obj不能为空!");
if (ObjectUtils.isWrapperOrPrimitive(obj)) {
return obj;
}
return getFieldValue(getField(obj.getClass(), fieldName), obj);
}
/**
* 获取指定对象中指定字段路径的值(类似js访问对象属性)
* 如:Product p = new Product(new User())
* 可使用ReflectionUtils.getValueByFieldPath(p, "user.name")获取到用户的name属性
*
* @param obj 取值对象
* @param fieldPath 字段路径(形如 user.name)
* @return 字段value
*/
public static Object getValueByFieldPath(Object obj, String fieldPath) {
String[] fieldNames = fieldPath.split("\\.");
Object result = null;
for (String fieldName : fieldNames) {
result = getFieldValue(obj, fieldName);
if (result == null) {
return null;
}
obj = result;
}
return result;
}
/**
* 设置字段值
*
* @param field 字段
* @param target 字段所属对象实例
* @param value 需要设置的值
*/
public static void setFieldValue(Field field, Object target, Object value) {
makeAccessible(field);
try {
field.set(target, value);
} catch (Exception e) {
throw new IllegalStateException(String.format("设置%s对象的%s字段值错误!"
, target.getClass().getName(), field.getName()), e);
}
}
/**
* 设置字段为可见
*
* @param field
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) ||
!Modifier.isPublic(field.getDeclaringClass().getModifiers()) ||
Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}
/**
* 调用无参数方法
*
* @param method 方法对象
* @param target 调用对象
* @return 执行结果
*/
public static Object invokeMethod(Method method, Object target) {
return invokeMethod(method, target, new Object[0]);
}
/**
* 调用指定对象的方法
*
* @param method 方法对象
* @param target 调用对象
* @param args 方法参数
* @return 执行结果
*/
public static Object invokeMethod(Method method, Object target, Object... args) {
try {
makeAccessible(method);
return method.invoke(target, args);
} catch (Exception ex) {
throw new IllegalStateException(String.format("执行%s.%s()方法错误!"
, target.getClass().getName(), method.getName()), ex);
}
}
/**
* 设置方法可见性
*
* @param method 方法
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
method.setAccessible(true);
}
}
/**
* 是否为equals方法
*
* @see Object#equals(Object)
*/
public static boolean isEqualsMethod(Method method) {
if (!"equals".equals(method.getName())) {
return false;
}
Class<?>[] paramTypes = method.getParameterTypes();
return (paramTypes.length == 1 && paramTypes[0] == Object.class);
}
/**
* 是否为hashCode方法
*
* @see Object#hashCode()
*/
public static boolean isHashCodeMethod(Method method) {
return "hashCode".equals(method.getName()) && method.getParameterCount() == 0;
}
/**
* 是否为Object的toString方法
*
* @see Object#toString()
*/
public static boolean isToStringMethod(Method method) {
return "toString".equals(method.getName()) && method.getParameterCount() == 0;
}
public static Method getMethodByName(Class<?> clazz, String name){
List<Field> fields = new ArrayList<>(32);
while (Object.class != clazz && clazz != null) {
// 获得该类所有声明的字段,即包括public、private和protected,但是不包括父类的申明字段,
// getFields:获得某个类的所有的公共(public)的字段,包括父类中的字段
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (StrUtil.isNotBlank(name) && method.getName().equals(name)) {
return method;
}
}
clazz = clazz.getSuperclass();
}
return null;
}
}
该源码来自https://gitee.com/objs/mayfly
作者博文:https://blog.csdn.net/mayfly_hml/article/details/88559139