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);
    }

    /**
     * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}

* 如: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>形式的对象中的下划线的键转成驼峰大小写的键的形式 * @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           值泛型
     * @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

你可能感兴趣的:(后端,java,开发语言)