Thymeleaf 长文本格式化、金额格式化

项目结构

image.png

gradle注意事项

//    compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'

compile("org.thymeleaf:thymeleaf:3.0.6.RELEASE")

compile group:'org.thymeleaf',name:'thymeleaf-spring4',version:'3.0.6.RELEASE'

compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.2.2")

如果引用'spring-boot-starter-thymelea'将默认使用Thymeleaf2而不是3

代码


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;


/**
 * Created by think on 2017/7/5.
 */
@Configuration
public class ThymeleafConfigure {

    @Bean
    public ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setTemplateMode("HTML5");
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCharacterEncoding("utf-8");
        templateResolver.setOrder(1);
        templateResolver.setCacheable(false);
        templateResolver.setApplicationContext(UnionpayApplication.getApplicationContext()); //这里是你的自己的MIANf方法入口
        return templateResolver;
    }


    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        engine.addDialect(new UnionPayDialect());
        return engine;
    }

}
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressionParser;
import org.thymeleaf.util.EvaluationUtils;
import org.thymeleaf.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static org.thymeleaf.util.StringUtils.trim;
import static org.thymeleaf.util.Validate.notEmpty;
import static org.thymeleaf.util.Validate.notNull;

/**
 * Created by think on 2017/7/5.
 */
public final class ThymeleafFacade {
    private ThymeleafFacade() {
        throw new UnsupportedOperationException();
    }

    public static String getRawValue(final IProcessableElementTag element, final AttributeName attributeName) {
        notNull(element, "element must not be null");
        notNull(attributeName, "attributeName must not be empty");

        final String rawValue = trim(element.getAttributeValue(attributeName));
        notEmpty(rawValue, "value of '" + attributeName + "' must not be empty");

        return rawValue;
    }

    public static String getRawValue(final IProcessableElementTag element, final String attributeName) {
        notNull(element, "element must not be null");
        notEmpty(attributeName, "attributeName must not be empty");

        final String rawValue = trim(element.getAttributeValue(attributeName));
        notEmpty(rawValue, "value of '" + attributeName + "' must not be empty");

        return rawValue;
    }

    public static Object evaluateExpression(ITemplateContext arguments, String expression) throws TemplateProcessingException {
        notNull(arguments, "arguments must not be null");
        notEmpty(expression, "expression must not be empty");

        final IStandardExpressionParser parser = new StandardExpressionParser();

        final IStandardExpression evaluableExpression = parser.parseExpression(arguments, expression);

        return evaluableExpression.execute(arguments);
    }

    public static List<Object> evaluateAsIterable(ITemplateContext arguments, String rawValue) throws TemplateProcessingException {
        notNull(arguments, "arguments must not be null");
        notEmpty(rawValue, "rawValue must not be empty");

        final Object evaluatedExpression = evaluateExpression(arguments, rawValue);

        return EvaluationUtils.evaluateAsList(evaluatedExpression);
    }

    public static List<Object> evaluateAsIterableOrRawValue(ITemplateContext arguments, String rawValue) {
        notNull(arguments, "arguments must not be null");
        notEmpty(rawValue, "rawValue must not be empty");

        final List<Object> result = new ArrayList<Object>();
        try {
            result.addAll(evaluateAsIterable(arguments, rawValue));
        } catch (TemplateProcessingException ex) {
            result.add(rawValue);
        }

        return unmodifiableList(result);
    }

    public static List<String> evaluateAsStringsWithDelimiter(ITemplateContext arguments, String rawValue, String delimiter) {
        notNull(arguments, "arguments must not be null");

        notEmpty(rawValue, "rawValue must not be empty");
        notEmpty(delimiter, "delimiter must not be empty");

        final List<String> result = new ArrayList<String>();
        final List<Object> iterates = evaluateAsIterableOrRawValue(arguments, rawValue);

        for (Object o : iterates) {
            result.addAll(asList(StringUtils.split(o, delimiter)));
        }

        return unmodifiableList(result);
    }
}
import org.thymeleaf.dialect.AbstractProcessorDialect;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.standard.StandardDialect;

import java.util.HashSet;
import java.util.Set;

/**
 * Created by think on 2017/7/5.
 */
public class UnionPayDialect extends AbstractProcessorDialect {

    private static final String DIALECT_NAME = "UnionPay Dialect";


    public UnionPayDialect() {
        // We will set this dialect the same "dialect processor" precedence as
        // the Standard Dialect, so that processor executions can interleave.
        super(DIALECT_NAME, "unionpay", StandardDialect.PROCESSOR_PRECEDENCE);
    }

    @Override
    public Set<IProcessor> getProcessors(String dialectPrefix) {
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add(new UnionpayStringProcessor(dialectPrefix));
        processors.add(new UnionpayNumberProcessor(dialectPrefix));

        return processors;
    }
}
import org.apache.commons.lang3.StringUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.templatemode.TemplateMode;
import org.unbescape.html.HtmlEscape;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.List;

import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.evaluateAsStringsWithDelimiter;
import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.getRawValue;

/**
 * Created by think on 2017/7/5.
 */
public class UnionpayNumberProcessor extends AbstractAttributeTagProcessor {

    private static final String DELIMITER = ",";

    private static final String ATTR_NAME = "number";
    private static final int PRECEDENCE = 100;




    private static final String F2Y="moneyF2y"; //金额分转元保留2位小数

    public UnionpayNumberProcessor(final String dialectPrefix) {
        super(
                TemplateMode.HTML, // This processor will apply only to HTML mode
                dialectPrefix,     // Prefix to be applied to name for matching
                null,              // No tag name: match any tag name
                false,             // No prefix to be applied to tag name
                ATTR_NAME,         // Name of the attribute that will be matched
                true,              // Apply dialect prefix to attribute name
                PRECEDENCE,        // Precedence (inside dialect's own precedence)
                true);             // Remove the matched attribute afterwards
    }

       @Override
        protected void doProcess(ITemplateContext context,
                                 IProcessableElementTag tag, AttributeName attributeName,
                                 String attributeValue, IElementTagStructureHandler structureHandler) {

            final String rawValue = getRawValue(tag, attributeName); //获取标签内容表达式
            String method=null;
            String exper=null;
            if(StringUtils.isNotEmpty(rawValue)){
                method=rawValue.split(":")[0];  //获取类型
                exper=rawValue.split(":")[1]; //获取表达式
            }
            //通过IStandardExpression 解析器 解析表达式获取参数
            final List<String> values = evaluateAsStringsWithDelimiter(context, exper, DELIMITER);
            final String elementCompleteName = tag.getElementCompleteName(); //标签名
            //创建模型
            final IModelFactory modelFactory = context.getModelFactory();
            final IModel model = modelFactory.createModel();
            //添加模型 标签
            model.add(modelFactory.createOpenElementTag(elementCompleteName));
            for (String value : values) {
                //创建 html5标签 文本返回数据
                if(F2Y.equalsIgnoreCase(method)){
                    try{
                        double n = NumberFormat.getInstance().parse(value).doubleValue();
                        model.add(modelFactory.createText(HtmlEscape.escapeHtml5(formatMoney(n))));
                    }catch (ParseException e){
                        model.add(modelFactory.createText(HtmlEscape.escapeHtml5("0")));
                    }

                }
            }
            //添加模型 标签
            model.add(modelFactory.createCloseElementTag(elementCompleteName));
            //替换页面标签
            structureHandler.replaceWith(model, false);
        }

    private String formatMoney(double value) {
        DecimalFormat df = new DecimalFormat("0.00");//格式化小数,不足的补0
        String v = df.format(value/100);//返回的是String类型的
        return v;
    }


}

import org.apache.commons.lang3.StringUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.templatemode.TemplateMode;
import org.unbescape.html.HtmlEscape;

import java.util.List;

import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.evaluateAsStringsWithDelimiter;
import static cn.gotoil.unionpay.config.thymeleaf.ThymeleafFacade.getRawValue;

/**
 * Created by think on 2017/7/5.
 */
public class UnionpayStringProcessor extends AbstractAttributeTagProcessor {

    private static final String DELIMITER = ",";

    private static final String ATTR_NAME = "string";
    private static final int PRECEDENCE = 100;


    private static final String LongTextFormat = "longTextFormat";
    private static final String CARD = "card";
    private static final String MOBILE = "mobile";
    private static final String IDENTITY = "identity";
    private static final String CSN = "csn";

    public UnionpayStringProcessor(final String dialectPrefix) {
        super(
                TemplateMode.HTML, // This processor will apply only to HTML mode
                dialectPrefix,     // Prefix to be applied to name for matching
                null,              // No tag name: match any tag name
                false,             // No prefix to be applied to tag name
                ATTR_NAME,         // Name of the attribute that will be matched
                true,              // Apply dialect prefix to attribute name
                PRECEDENCE,        // Precedence (inside dialect's own precedence)
                true);             // Remove the matched attribute afterwards
    }

    @Override
    protected void doProcess(ITemplateContext context,
                             IProcessableElementTag tag, AttributeName attributeName,
                             String attributeValue, IElementTagStructureHandler structureHandler) {

        final String rawValue = getRawValue(tag, attributeName); //获取标签内容表达式
        String method = null;
        String exper = null;
        if (StringUtils.isNotEmpty(rawValue)) {
            method = rawValue.split(":")[0];  //获取类型
            exper = rawValue.split(":")[1]; //获取表达式
        }
        //通过IStandardExpression 解析器 解析表达式获取参数
        final List<String> values = evaluateAsStringsWithDelimiter(context, exper, DELIMITER);
        final String elementCompleteName = tag.getElementCompleteName(); //标签名
        //创建模型
        final IModelFactory modelFactory = context.getModelFactory();
        final IModel model = modelFactory.createModel();
        //添加模型 标签
        model.add(modelFactory.createOpenElementTag(elementCompleteName));
        for (String value : values) {
            //创建 html5标签 文本返回数据
            if (CARD.equals(method)) {
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getCardNo(value))));
            } else if (MOBILE.equals(method)) {
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getMobile(value))));
            } else if (IDENTITY.equals(method)) {
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getIdentity(value))));
            } else if (CSN.equals(method)) {
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getCsn(value))));
            } else if (LongTextFormat.equals(method)) {
                int precLen = StringUtils.isEmpty(tag.getAttributeValue("prec")) ? 10 : Integer.parseInt(tag.getAttributeValue("prec"));
                model.add(modelFactory.createText(HtmlEscape.escapeHtml5(getLongTextFormat(value, precLen))));
            }
        }
        //添加模型 标签
        model.add(modelFactory.createCloseElementTag(elementCompleteName));
        //替换页面标签
        structureHandler.replaceWith(model, false);
    }

    private String getLongTextFormat(String value, int i) {
        if (StringUtils.isEmpty(value) || value.length() < i) {
            return value;
        } else {
            return value.substring(0, i) + "...";
        }
    }

    protected String getCardNo(String cardNo) {
        if (StringUtils.isNotBlank(cardNo) && cardNo.length() >= 9) {
            return cardNo.substring(0, 4) + cardNo.substring(4, cardNo.length() - 3).replaceAll("[0-9]", "*") + cardNo.substring(cardNo.length() - 4, cardNo.length());
        }
        return cardNo;
    }

    protected static String getIdentity(String val) {
        if (StringUtils.isEmpty(val) || val.length() < 9) {
            return val;
        } else {
            return val.substring(0, 4) + val.substring(4, val.length() - 4).replaceAll("[0-9]", "*") + val.substring(val.length() - 4, val.length());
        }
    }

    /**
     * 前四后四显示
     *
     * @param val
     * @return
     */
    protected static String getMobile(String val) {
        if (StringUtils.isEmpty(val) || val.length() < 9) {
            return val;
        } else {
            return val.substring(0, 3) + val.substring(4, val.length() - 3).replaceAll("[0-9]", "*") + val.substring(val.length() - 4, val.length());
        }
    }

    /**
     * 星星显示
     *
     * @param val
     * @return
     */
    protected String getCsn(String val) {
        if (StringUtils.isEmpty(val) || val.length() < 12) {
            return val;
        } else {
            return val.substring(0, 2) + val.substring(2, val.length() - 3).replaceAll("[0-9a-zA-Z]", "*") + val.substring(val.length() - 6, val.length());
        }
    }

}

页面使用

<div unionpay:number="moneyF2y:${bean.orderFee}" ></div>

 <div  unionpay:string="longTextFormat:${bean.appName}"  prec=10 th:title="${bean.appName}" ></div>  //这里的prec 是指保留的长度 如果不设置也会有默认值

其他

  我这里是处理我自己的业务。希望能帮到大家。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容