极简模板语言实现
在項目中遇到這樣一個功能,需要用戶輸入一段帶參數的字符串模板,然后服務端根據一個上下文去解析它。查了一些資料發現了StrSubstitutor 這個小工具。其自帶例子如下:
Map valuesMap = HashMap();valuesMap.put("animal", "quick brown fox");valuesMap.put("target", "lazy dog");String templateString = "The ${animal} jumped over the ${target}.";StrSubstitutor sub = new StrSubstitutor(valuesMap);String resolvedString = sub.replace(templateString);現在我想從一個二維表數據結構(List<Map<String, Object>>)中的某一行都去跑這么一個模板,這樣上面的例子就不需要了。我甚至還想對結果做個擴展,比如格式化什么的。通過查詢相關資料, 發現了StrLookup這個類。結合正則表達式,有了下面的實現
public class ExtraFormatLookup extends StrLookup {private static final String FMT = "fmt";private final List<Map<String, Object>> result;private final Map<String, MetricDO> metrics;ExtraFormatLookup(List<Map<String, Object>> result, Map<String, MetricDO> metrics) {this.result = result;this.metrics = metrics;}@Overridepublic String lookup(String s) {s = s.toLowerCase();String regex = "([^\\[.]+)(?:\\[(-?\\d+)])?(?:\\.([a-zA-z]+)(?::-(.*))?)?";Matcher matcher = Pattern.compile(regex).matcher(s);if (matcher.find()) {String key = matcher.group(1);String indexStr = matcher.group(2);String suffix = matcher.group(3);String defaultValue = matcher.group(4) == null ? "" : matcher.group(4);if (result == null || result.isEmpty()) {return defaultValue;}int index = 0;if (!Strings.isNullOrEmpty(indexStr)) {index = Integer.parseInt(indexStr);if (index < 0 - result.size() || index >= result.size()) {return defaultValue;}if (index < 0) {index = result.size() + index;}}String r = String.valueOf(result.get(index).getOrDefault(key, defaultValue));if (metrics.containsKey(key) && FMT.equalsIgnoreCase(suffix)) {r = CommonUtil.getValueFormatStr(metrics.get(key), r);}return r;} else {return "";}} }測試代碼如下:
@Testpublic void lookup1() {List<Map<String, Object>> result = Lists.newArrayList(ImmutableMap.of("abc", "1111111"),ImmutableMap.of("abc", "222222"),ImmutableMap.of("abc", "33333", "def", "4444"));Map<String, MetricDO> metrics = Maps.newHashMap();metrics.put("abc", new MetricDO().setCode("abc").setType("abs").setPattern(",###,###"));StrSubstitutor sub = new StrSubstitutor(new ExtraFormatLookup(result, metrics));assertThat(sub.replace("test ${abc}"), Matchers.equalTo("test 1111111"));assertThat(sub.replace("test ${abc.fmt}"), Matchers.equalTo("test 1,111,111"));assertThat(sub.replace("test ${abc[0]}"), Matchers.equalTo("test 1111111"));assertThat(sub.replace("test ${abc[1]}"), Matchers.equalTo("test 222222"));assertThat(sub.replace("test ${abc[2]}"), Matchers.equalTo("test 33333"));assertThat(sub.replace("test ${abc[1].fmt}"), Matchers.equalTo("test 222,222"));assertThat(sub.replace("test ${abc[2].fmt}"), Matchers.equalTo("test 33,333"));assertThat(sub.replace("test ${def[2].fmt}"), Matchers.equalTo("test 4444"));assertThat(sub.replace("test ${def.fmt}"), Matchers.equalTo("test "));assertThat(sub.replace("test ${abc[-1].fmt}"), Matchers.equalTo("test 33,333"));assertThat(sub.replace("test ${abc[-2].fmt}"), Matchers.equalTo("test 222,222"));assertThat(sub.replace("test ${abc[-5].fmt}"), Matchers.equalTo("test "));assertThat(sub.replace("test ${abcd[-5].fmt:-efg}"), Matchers.equalTo("test efg"));assertThat(sub.replace("test ${abc[-789].fmt:-efg}"), Matchers.equalTo("test efg"));assertThat(sub.replace("test ${abcd[1111].fmt:-efg}"), Matchers.equalTo("test efg"));}其實邏輯很簡單,就是通過正則表達式拿到要訪問的key,row的index,擴展方法字段以及默認值。后續要擴展也比較簡單,事先實現好方法然后增加支持的擴展字段就好了。
另外安利一個驗證Java正則表達式的網站https://www.freeformatter.com/java-regex-tester.html#ad-output
總結
- 上一篇: 观察者模式 Observer 发布订阅模
- 下一篇: 记一次 react 15.3.1 老项目