新增方法解析为 {{key}} 格式的 json

This commit is contained in:
yuejiajun 2025-09-28 12:01:37 +08:00
parent 03ab867c4f
commit 175fbf93a2

View File

@ -1,8 +1,14 @@
package com.example.demo.draft.demo043.util;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -72,6 +78,7 @@ import java.util.regex.Pattern;
* <p><b>创建时间</b> 2024年</p>
* <p><b>维护者</b> demo043项目组</p>
*/
@Slf4j
public class FormatUtil {
/**
@ -1371,17 +1378,234 @@ public class FormatUtil {
}
/**
* 偷梁换柱 - 支持正则表达式和多种替换选项
* @param source 源字符串
* @param replacementMap 替换映射关系
* @return 替换后的字符串
* 字符串批量替换工具方法
*
* <p>该方法提供强大的字符串替换功能支持正则表达式和普通字符串替换
* 可灵活控制大小写敏感和整词匹配等选项方法名"偷梁换柱"形象地描述了替换功能</p>
*
* <p>功能特性</p>
* <ul>
* <li>支持正则表达式替换和普通字符串替换</li>
* <li>可配置大小写敏感匹配</li>
* <li>支持整词匹配word boundary</li>
* <li>提供正则表达式错误降级处理</li>
* <li>支持批量替换操作</li>
* </ul>
*
* @param source 源字符串如果为null则直接返回null
* @param replacementMap 替换映射表Key为要查找的模式正则表达式或普通字符串
* Value为替换内容如果为null或空则返回原字符串
* @param caseSensitive 是否大小写敏感
* true: 区分大小写false: 不区分大小写
* @param wholeWord 是否整词匹配
* true: 只匹配完整单词false: 匹配任意位置
* @param useRegex 是否使用正则表达式
* true: 将key作为正则表达式处理false: 作为普通字符串处理
* @return 替换后的字符串如果源字符串或替换映射表为null/空则返回原字符串
* @throws IllegalArgumentException 当replacementMap包含null键或值时抛出
*/
public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap) {
return stealBeamsAndReplacePillars(source, replacementMap, false, false);
public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap,
boolean caseSensitive, boolean wholeWord, boolean useRegex) {
// 参数校验如果源字符串或替换映射表为空直接返回原字符串
if (source == null || source.isEmpty()) {
log.debug("源字符串为空,直接返回");
return source;
}
if (replacementMap == null || replacementMap.isEmpty()) {
log.debug("替换映射表为空,直接返回原字符串");
return source;
}
// 检查替换映射表中是否包含null键或值
for (Map.Entry<String, String> entry : replacementMap.entrySet()) {
if (entry.getKey() == null) {
throw new IllegalArgumentException("替换映射表中不能包含null键");
}
if (entry.getValue() == null) {
log.warn("替换映射表中键 '{}' 的值为null将作为空字符串处理", entry.getKey());
}
}
log.info("开始字符串替换处理,源字符串长度: {}, 替换规则数量: {}, 大小写敏感: {}, 整词匹配: {}, 使用正则: {}",
source.length(), replacementMap.size(), match(caseSensitive), match(wholeWord), match(useRegex));
String result = source;
int totalReplacements = 0;
// 遍历替换映射表按顺序应用每个替换规则
for (Map.Entry<String, String> entry : replacementMap.entrySet()) {
String patternStr = entry.getKey();
String replacement = entry.getValue() != null ? entry.getValue() : "";
try {
if (useRegex) {
// 使用正则表达式替换
log.info("应用替换规则(正则表达式替换): '{}' -> '{}'", patternStr, replacement);
result = applyRegexReplacement(result, patternStr, replacement, caseSensitive, wholeWord);
} else {
// 使用普通字符串替换
log.info("应用替换规则(普通字符串替换): '{}' -> '{}'", patternStr, replacement);
result = applyLiteralReplacement(result, patternStr, replacement, caseSensitive, wholeWord);
}
// 统计替换次数通过比较字符串长度变化估算
if (!result.equals(source)) {
totalReplacements++;
}
} catch (Exception e) {
log.error("替换规则处理失败 - 模式: '{}', 替换值: '{}', 错误: {}",
patternStr, replacement, e.getMessage());
// 发生错误时继续处理下一个规则不中断整个流程
}
}
log.info("字符串替换完成,总应用规则数: {}, 实际产生变化的规则数: {}",
replacementMap.size(), totalReplacements);
return result;
}
/**
* 偷梁换柱 - 支持正则表达式和多种替换选项
* 应用正则表达式替换
*
* @param source 源字符串
* @param regex 正则表达式模式
* @param replacement 替换内容
* @param caseSensitive 是否大小写敏感
* @param wholeWord 是否整词匹配
* @return 替换后的字符串
*/
private static String applyRegexReplacement(String source, String regex, String replacement,
boolean caseSensitive, boolean wholeWord) {
String processedRegex = regex;
// 处理整词匹配如果启用整词匹配且正则表达式尚未包含单词边界
if (wholeWord && !processedRegex.startsWith("\\b") && !processedRegex.contains("\\b")) {
processedRegex = "\\b" + processedRegex + "\\b";
log.info("添加单词边界,处理后的正则: {}", processedRegex); // trace
}
try {
// 设置正则表达式标志
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
Pattern pattern = Pattern.compile(processedRegex, flags);
Matcher matcher = pattern.matcher(source);
String result = matcher.replaceAll(replacement);
// 记录替换详情调试级别
if (log.isTraceEnabled() && !result.equals(source)) {
int count = 0;
matcher.reset();
while (matcher.find()) count++;
log.info("正则替换完成: '{}' 匹配 {} 次", regex, count); // trace
}
return result;
} catch (Exception e) {
log.warn("正则表达式编译或执行失败,降级为普通字符串替换 - 模式: '{}', 错误: {}",
regex, e.getMessage());
// 降级处理使用普通字符串替换
return applyLiteralReplacement(source, regex, replacement, caseSensitive, wholeWord);
}
}
/**
* 应用普通字符串替换
*
* @param source 源字符串
* @param literal 要查找的字符串
* @param replacement 替换内容
* @param caseSensitive 是否大小写敏感
* @param wholeWord 是否整词匹配
* @return 替换后的字符串
*/
private static String applyLiteralReplacement(String source, String literal, String replacement,
boolean caseSensitive, boolean wholeWord) {
if (wholeWord) {
// 整词匹配处理使用正则表达式实现单词边界匹配
try {
String regex = "\\b" + Pattern.quote(literal) + "\\b";
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
Pattern pattern = Pattern.compile(regex, flags);
return pattern.matcher(source).replaceAll(replacement);
} catch (Exception e) {
log.warn("整词匹配处理失败,使用普通替换 - 模式: '{}'", literal);
// 降级为普通替换
}
}
// 普通字符串替换
if (caseSensitive) {
// 大小写敏感替换
return source.replace(literal, replacement);
} else {
// 大小写不敏感替换需要手动实现
return replaceIgnoreCase(source, literal, replacement);
}
}
/**
* 不区分大小写的字符串替换
*
* @param source 源字符串
* @param target 要查找的字符串
* @param replacement 替换内容
* @return 替换后的字符串
*/
private static String replaceIgnoreCase(String source, String target, String replacement) {
if (source == null || target == null || replacement == null) {
return source;
}
StringBuilder result = new StringBuilder();
String lowerSource = source.toLowerCase();
String lowerTarget = target.toLowerCase();
int index = 0;
int targetLength = target.length();
int count = 0;
while (true) {
int pos = lowerSource.indexOf(lowerTarget, index);
if (pos == -1) break;
// 添加不匹配部分
result.append(source, index, pos);
// 添加替换内容
result.append(replacement);
index = pos + targetLength;
count++;
}
// 添加剩余部分
result.append(source.substring(index));
if (count > 0) {
log.info("不区分大小写替换完成: '{}' 匹配 {} 次", target, count); // trace
}
return result.toString();
}
/**
* 偷梁换柱 - 使用默认参数使用正则
* @param source 源字符串
* @param replacementMap 替换映射关系
* @param caseSensitive 是否区分大小写
* @param wholeWord 是否整词匹配
* @return 替换后的字符串
*/
public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap,
boolean caseSensitive, boolean wholeWord) {
return stealBeamsAndReplacePillars(source, replacementMap, caseSensitive, wholeWord, true);
}
/**
* 偷梁换柱 - 使用默认参数非整词匹配使用正则
* @param source 源字符串
* @param replacementMap 替换映射关系
* @param caseSensitive 是否区分大小写
@ -1392,41 +1616,184 @@ public class FormatUtil {
}
/**
* 偷梁换柱
* 偷梁换柱 - 使用默认参数大小写不敏感非整词匹配使用正则
*
* @param source 源字符串
* @param replacementMap 替换映射关系
* @param caseSensitive 是否区分大小写
* @param wholeWord 是否整词匹配
* @return 替换后的字符串
*/
public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap,
boolean caseSensitive, boolean wholeWord) {
if (source == null || replacementMap == null || replacementMap.isEmpty()) {
return source;
public static String stealBeamsAndReplacePillars(String source, Map<String, String> replacementMap) {
return stealBeamsAndReplacePillars(source, replacementMap, false, false, true);
}
String result = source;
// ========== JSON对象再次加工 ==========
for (Map.Entry<String, String> entry : replacementMap.entrySet()) {
String regex = entry.getKey();
String replacement = entry.getValue();
// 处理整词匹配
if (wholeWord && !regex.startsWith("\\b")) {
regex = "\\b" + regex + "\\b";
/**
* 加载并预处理JSON文件
*
* <p>该方法读取JSON文件内容解析为对象列表并对所有键名进行标准化处理
* 将键名转换为双花括号格式{{key}}主要用于模板处理或数据转换场景</p>
*
* <p>处理流程</p>
* <ol>
* <li>读取JSON文件内容为字符串</li>
* <li>使用FastJSON库解析为对象列表</li>
* <li>递归处理所有对象的键名转换为{{key}}格式</li>
* </ol>
*
* @param processJSONFile JSON文件路径不能为null或空字符串
* @return 包含预处理后对象的列表列表中的每个对象的所有键名都已转换为{{key}}格式
* @throws IOException 当文件读取失败文件不存在或文件格式错误时抛出
* @throws IllegalArgumentException 当processJSONFile为null或空字符串时抛出
*/
public static List<Object> loadJSON(String processJSONFile) throws IOException {
// 参数校验
if (processJSONFile == null || processJSONFile.trim().isEmpty()) {
throw new IllegalArgumentException("JSON文件路径不能为空或null");
}
log.info("开始加载JSON文件: {}", processJSONFile);
try {
int flags = caseSensitive ? 0 : Pattern.CASE_INSENSITIVE;
Pattern pattern = Pattern.compile(regex, flags);
Matcher matcher = pattern.matcher(result);
result = matcher.replaceAll(replacement);
// Step 1: 读取JSON文件内容
log.debug("读取文件内容...");
String targetContent = Files.readString(Path.of(processJSONFile), StandardCharsets.UTF_8);
if (targetContent == null || targetContent.trim().isEmpty()) {
log.warn("JSON文件内容为空: {}", processJSONFile);
return new ArrayList<>();
}
log.debug("文件内容读取成功,长度: {} 字符", targetContent.length());
// Step 2: 解析JSON字符串为对象列表
log.debug("解析JSON内容...");
List<Object> list = JSON.parseArray(targetContent);
if (list == null) {
log.warn("JSON解析结果为null返回空列表");
return new ArrayList<>();
}
log.debug("JSON解析成功对象数量: {}", list.size());
// Step 3: 处理所有对象的键名转换为{{key}}格式
log.debug("开始处理键名格式转换...");
List<Object> processedList = processKeysToMustacheFormat(list);
log.info("JSON文件加载完成共处理 {} 个对象", processedList.size());
return processedList;
} catch (IOException e) {
log.error("读取JSON文件失败: {}", processJSONFile, e);
throw e;
} catch (Exception e) {
System.err.println("正则表达式错误: " + regex + " - " + e.getMessage());
log.error("解析JSON内容失败: {}", processJSONFile, e);
throw new IOException("JSON解析失败: " + e.getMessage(), e);
}
}
return result;
/**
* 递归处理对象中的所有键名转换为双花括号格式
*
* <p>该方法会遍历对象的所有层级将每个键名从"key"格式转换为"{{key}}"格式</p>
*
* @param originalList 原始对象列表
* @return 键名转换后的新对象列表
*/
private static List<Object> processKeysToMustacheFormat(List<Object> originalList) {
if (originalList == null || originalList.isEmpty()) {
return originalList;
}
List<Object> processedList = new ArrayList<>(originalList.size());
for (int i = 0; i < originalList.size(); i++) {
try {
Object item = originalList.get(i);
Object processedItem = processSingleObject(item);
processedList.add(processedItem);
if (log.isDebugEnabled() && i % 100 == 0) {
log.debug("已处理 {} 个对象", i);
}
} catch (Exception e) {
log.warn("处理第 {} 个对象时发生异常,跳过该对象", i, e);
// 发生异常时保留原始对象
processedList.add(originalList.get(i));
}
}
return processedList;
}
/**
* 处理单个对象的键名转换
*
* @param obj 要处理的对象支持MapList和基本类型
* @return 键名转换后的新对象
*/
private static Object processSingleObject(Object obj) {
if (obj instanceof Map) {
// 处理Map对象
@SuppressWarnings("unchecked")
Map<String, Object> originalMap = (Map<String, Object>) obj;
Map<String, Object> processedMap = new HashMap<>(originalMap.size());
for (Map.Entry<String, Object> entry : originalMap.entrySet()) {
String originalKey = entry.getKey();
Object value = entry.getValue();
// 转换键名格式key -> {{key}}
String processedKey = convertKeyToMustacheFormat(originalKey);
// 递归处理值
Object processedValue = processSingleObject(value);
processedMap.put(processedKey, processedValue);
}
return processedMap;
} else if (obj instanceof List) {
// 处理List对象
@SuppressWarnings("unchecked")
List<Object> originalList = (List<Object>) obj;
List<Object> processedList = new ArrayList<>(originalList.size());
for (Object item : originalList) {
processedList.add(processSingleObject(item));
}
return processedList;
} else {
// 基本类型StringNumberBoolean等直接返回
return obj;
}
}
/**
* 将键名转换为双花括号格式
*
* <p>如果键名已经包含花括号则不再重复添加</p>
*
* @param key 原始键名
* @return 转换后的键名格式为{{key}}
*/
private static String convertKeyToMustacheFormat(String key) {
if (key == null || key.trim().isEmpty()) {
log.warn("遇到空键名,返回原始值");
return key;
}
// 如果键名已经是{{key}}格式则不再处理
if (key.startsWith("{{") && key.endsWith("}}")) {
return key;
}
// 转换为{{key}}格式
return "{{" + key + "}}";
}
private static String match(boolean flag) {
return flag ? "启用" : "禁用";
}
}