Java字符串模板解析

最近遇到一个需求,就是给出一个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);
    }

    /**
     * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}<br/><br/>
     * 如:template = category:${}:product:${}<br/>
     * values = {"1", "2"}<br/>
     * 返回 category:1:product:2<br/>
     *
     * @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();
    }

    /**
     * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}<br/><br/>
     * 如:template = category:${}:product:${}<br/>
     * values = {"1", "2"}<br/>
     * 返回 category:1:product:2<br/>
     *
     * @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为占位符中的内容。<br/><br/>
     * 如:content = product:${id}:detail:${did}<br/>
     * valueMap = id -> 1; pid -> 2<br/>
     * 经过解析返回 product:1:detail:2<br/>
     *
     * @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访问对象属性值)替换模板中的占位符 <br/><br/>
     * 如 content = product:${id}:detail:${detail.id} <br/>
     * obj = Product.builder().id(1).detail(Detail.builder().id(2).build()).build(); <br/>
     * 经过解析返回 product:1:detail:2 <br/>
     *
     * @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里面的值,如果没有取到对应的值,则不做任何修改, 这个方法是我自己写的,其他的方法都是大佬写的<br/>
     * 如: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<Map<String,Object>>形式的对象中的下划线的键转成驼峰大小写的键的形式
     * @param obj
     * @return
     */
    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 <T>          值泛型
     * @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)<br/>
     *
     * @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访问对象属性) <br/>
     * 如:Product p = new Product(new User())  <br/>
     * 可使用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

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值