追加注释
This commit is contained in:
parent
b356735d72
commit
03ab867c4f
@ -12,39 +12,97 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 模板文件工具类
|
||||
* 负责从文件系统或类路径加载模板文件
|
||||
* 负责从文件系统或类路径加载模板文件,支持缓存机制提高性能
|
||||
*
|
||||
* <p>该类提供了以下功能:</p>
|
||||
* <ul>
|
||||
* <li>从类路径加载模板文件</li>
|
||||
* <li>从文件系统加载模板文件</li>
|
||||
* <li>智能识别路径前缀自动选择加载方式</li>
|
||||
* <li>模板内容缓存,避免重复读取文件</li>
|
||||
* <li>缓存管理功能</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>使用示例:</p>
|
||||
* <pre>
|
||||
* // 从类路径加载
|
||||
* String template1 = TemplateFileLoader.loadTemplate("classpath:templates/default.html");
|
||||
* // 从文件系统加载
|
||||
* String template2 = TemplateFileLoader.loadTemplate("file:/opt/templates/default.html");
|
||||
* // 默认从类路径加载
|
||||
* String template3 = TemplateFileLoader.loadTemplate("templates/default.html");
|
||||
* </pre>
|
||||
*
|
||||
* @author 岳佳君
|
||||
* @version 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class TemplateFileLoader {
|
||||
|
||||
/**
|
||||
* 默认模板目录路径
|
||||
*/
|
||||
private static final String DEFAULT_TEMPLATE_DIR = "templates/";
|
||||
|
||||
/**
|
||||
* 类路径前缀标识符
|
||||
*/
|
||||
private static final String CLASS_PATH_PREFIX = "classpath:";
|
||||
|
||||
/**
|
||||
* 文件系统路径前缀标识符
|
||||
*/
|
||||
private static final String FILE_PATH_PREFIX = "file:";
|
||||
|
||||
// 模板缓存
|
||||
/**
|
||||
* 模板缓存,使用HashMap存储已加载的模板内容
|
||||
* Key: 缓存键(包含路径前缀和模板路径)
|
||||
* Value: 模板文件内容
|
||||
*/
|
||||
private static final Map<String, String> TEMPLATE_CACHE = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 从类路径加载模板文件
|
||||
*
|
||||
* <p>该方法会首先检查缓存中是否已存在该模板,如果存在则直接返回缓存内容,
|
||||
* 否则从类路径读取文件内容并存入缓存。</p>
|
||||
*
|
||||
* @param templatePath 模板文件路径,可以是相对路径或绝对路径
|
||||
* (相对路径相对于类路径根目录)
|
||||
* @return 模板文件的内容字符串
|
||||
* @throws IOException 当模板文件未找到或读取失败时抛出
|
||||
* @throws IllegalArgumentException 当templatePath为null或空字符串时抛出
|
||||
*/
|
||||
public static String loadTemplateFromClasspath(String templatePath) throws IOException {
|
||||
// 参数校验
|
||||
if (templatePath == null || templatePath.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("模板路径不能为空");
|
||||
}
|
||||
|
||||
String cacheKey = CLASS_PATH_PREFIX + templatePath;
|
||||
|
||||
// 检查缓存中是否已存在该模板
|
||||
if (TEMPLATE_CACHE.containsKey(cacheKey)) {
|
||||
log.debug("从缓存加载类路径模板: {}", templatePath);
|
||||
return TEMPLATE_CACHE.get(cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
// 从类路径加载
|
||||
// 处理路径格式,确保以"/"开头
|
||||
String fullPath = templatePath.startsWith("/") ? templatePath : "/" + templatePath;
|
||||
java.io.InputStream inputStream = TemplateFileLoader.class.getResourceAsStream(fullPath);
|
||||
|
||||
// 检查文件是否存在
|
||||
if (inputStream == null) {
|
||||
throw new IOException("模板文件未找到: " + templatePath);
|
||||
}
|
||||
|
||||
// 读取文件内容并转换为字符串
|
||||
String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
|
||||
// 存入缓存
|
||||
TEMPLATE_CACHE.put(cacheKey, content);
|
||||
log.info("从类路径加载模板: {}", templatePath);
|
||||
log.info("从类路径加载模板成功: {}", templatePath);
|
||||
return content;
|
||||
|
||||
} catch (IOException e) {
|
||||
@ -55,22 +113,43 @@ public class TemplateFileLoader {
|
||||
|
||||
/**
|
||||
* 从文件系统加载模板文件
|
||||
*
|
||||
* <p>该方法会首先检查缓存中是否已存在该模板,如果存在则直接返回缓存内容,
|
||||
* 否则从文件系统读取文件内容并存入缓存。</p>
|
||||
*
|
||||
* @param filePath 模板文件的绝对路径或相对路径(相对于当前工作目录)
|
||||
* @return 模板文件的内容字符串
|
||||
* @throws IOException 当模板文件不存在或读取失败时抛出
|
||||
* @throws IllegalArgumentException 当filePath为null或空字符串时抛出
|
||||
*/
|
||||
public static String loadTemplateFromFileSystem(String filePath) throws IOException {
|
||||
// 参数校验
|
||||
if (filePath == null || filePath.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("文件路径不能为空");
|
||||
}
|
||||
|
||||
String cacheKey = FILE_PATH_PREFIX + filePath;
|
||||
|
||||
// 检查缓存中是否已存在该模板
|
||||
if (TEMPLATE_CACHE.containsKey(cacheKey)) {
|
||||
log.debug("从缓存加载文件系统模板: {}", filePath);
|
||||
return TEMPLATE_CACHE.get(cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
Path path = Paths.get(filePath);
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!Files.exists(path)) {
|
||||
throw new IOException("模板文件不存在: " + filePath);
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
String content = Files.readString(path, StandardCharsets.UTF_8);
|
||||
|
||||
// 存入缓存
|
||||
TEMPLATE_CACHE.put(cacheKey, content);
|
||||
log.info("从文件系统加载模板: {}", filePath);
|
||||
log.info("从文件系统加载模板成功: {}", filePath);
|
||||
return content;
|
||||
|
||||
} catch (IOException e) {
|
||||
@ -80,12 +159,31 @@ public class TemplateFileLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能加载模板(自动判断路径类型)
|
||||
* 智能加载模板文件(自动判断路径类型)
|
||||
*
|
||||
* <p>根据路径前缀自动选择加载方式:</p>
|
||||
* <ul>
|
||||
* <li>以"classpath:"开头:从类路径加载</li>
|
||||
* <li>以"file:"开头:从文件系统加载</li>
|
||||
* <li>无前缀:默认从类路径加载</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param templatePath 模板路径,支持带前缀的完整路径或不带前缀的相对路径
|
||||
* @return 模板文件的内容字符串
|
||||
* @throws IOException 当模板文件加载失败时抛出
|
||||
* @throws IllegalArgumentException 当templatePath为null或空字符串时抛出
|
||||
*/
|
||||
public static String loadTemplate(String templatePath) throws IOException {
|
||||
// 参数校验
|
||||
if (templatePath == null || templatePath.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("模板路径不能为空");
|
||||
}
|
||||
|
||||
if (templatePath.startsWith(CLASS_PATH_PREFIX)) {
|
||||
// 从类路径加载,去掉前缀
|
||||
return loadTemplateFromClasspath(templatePath.substring(CLASS_PATH_PREFIX.length()));
|
||||
} else if (templatePath.startsWith(FILE_PATH_PREFIX)) {
|
||||
// 从文件系统加载,去掉前缀
|
||||
return loadTemplateFromFileSystem(templatePath.substring(FILE_PATH_PREFIX.length()));
|
||||
} else {
|
||||
// 默认从类路径加载
|
||||
@ -94,17 +192,33 @@ public class TemplateFileLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除模板缓存
|
||||
* 清除所有模板缓存
|
||||
*
|
||||
* <p>该方法会清空当前内存中所有的模板缓存,适用于需要强制重新加载模板的场景,
|
||||
* 比如模板文件内容已更新但缓存还未过期的情况。</p>
|
||||
*/
|
||||
public static void clearCache() {
|
||||
int cacheSize = TEMPLATE_CACHE.size();
|
||||
TEMPLATE_CACHE.clear();
|
||||
log.info("模板缓存已清除");
|
||||
log.info("模板缓存已清除,原缓存大小: {}", cacheSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存中的模板数量
|
||||
* 获取当前缓存中的模板数量
|
||||
*
|
||||
* @return 缓存中模板的数量
|
||||
*/
|
||||
public static int getCacheSize() {
|
||||
return TEMPLATE_CACHE.size();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定路径的模板是否已缓存
|
||||
*
|
||||
* @param templatePath 模板路径(需要包含完整的前缀)
|
||||
* @return 如果模板已缓存返回true,否则返回false
|
||||
*/
|
||||
public static boolean isCached(String templatePath) {
|
||||
return TEMPLATE_CACHE.containsKey(templatePath);
|
||||
}
|
||||
}
|
@ -13,34 +13,87 @@ import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* XML转AFSIM格式转换器
|
||||
*
|
||||
* <p>该类提供将XML文件转换为JSON格式并应用正则表达式规则进行键名转换的功能。
|
||||
* 采用流式处理方式,适用于处理大型XML文件,避免内存溢出。</p>
|
||||
*
|
||||
* <p>主要功能:</p>
|
||||
* <ul>
|
||||
* <li>流式解析XML文件,降低内存占用</li>
|
||||
* <li>支持正则表达式规则对JSON键名进行转换</li>
|
||||
* <li>自动处理XML属性和元素内容</li>
|
||||
* <li>生成标准JSON数组格式输出</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author 岳佳君
|
||||
* @version 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class Xml2AFSIMTransformation {
|
||||
|
||||
/**
|
||||
* 正则表达式规则映射
|
||||
* Key: 编译后的正则表达式模式
|
||||
* Value: 替换字符串
|
||||
*/
|
||||
private final Map<Pattern, String> regexRules;
|
||||
|
||||
/**
|
||||
* 构造函数,初始化转换规则
|
||||
*
|
||||
* @param rules 转换规则映射,Key为匹配的正则表达式,Value为替换字符串
|
||||
* 如果为null,则使用空规则集
|
||||
*/
|
||||
public Xml2AFSIMTransformation(Map<String, String> rules) {
|
||||
log.info("初始化转换器,规则数量: {}", rules != null ? rules.size() : 0);
|
||||
log.info("初始化XML转AFSIM转换器,规则数量: {}", rules != null ? rules.size() : 0);
|
||||
// 编译正则表达式规则
|
||||
regexRules = new HashMap<>();
|
||||
if (rules != null) {
|
||||
for (Map.Entry<String, String> entry : rules.entrySet()) {
|
||||
// 预编译正则表达式以提高性能
|
||||
regexRules.put(Pattern.compile(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
log.debug("正则表达式规则编译完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 流式XML转JSON并应用转换规则
|
||||
* 将XML文件转换为JSON格式并应用转换规则
|
||||
*
|
||||
* <p>该方法使用StAX流式API解析XML,适用于处理大型文件。
|
||||
* 转换后的JSON格式为对象数组,每个XML根元素对应一个JSON对象。</p>
|
||||
*
|
||||
* @param xmlFile 输入的XML文件,不能为null
|
||||
* @param jsonFile 输出的JSON文件,不能为null
|
||||
* @param rootElement 根元素名称,用于标识每个JSON对象的开始和结束
|
||||
* @throws Exception 当文件读写失败、XML解析错误或转换过程中出现异常时抛出
|
||||
* @throws IllegalArgumentException 当参数为null或空字符串时抛出
|
||||
*/
|
||||
public void convertXmlToJson(File xmlFile, File jsonFile, String rootElement) throws Exception {
|
||||
// 参数校验
|
||||
if (xmlFile == null || !xmlFile.exists()) {
|
||||
throw new IllegalArgumentException("XML文件不存在或为null: " + xmlFile);
|
||||
}
|
||||
if (jsonFile == null) {
|
||||
throw new IllegalArgumentException("JSON文件路径不能为null");
|
||||
}
|
||||
if (rootElement == null || rootElement.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("根元素名称不能为空");
|
||||
}
|
||||
|
||||
log.info("开始XML转JSON转换并应用规则");
|
||||
log.debug("输入文件: {}, 输出文件: {}, 根元素: {}", xmlFile.getPath(), jsonFile.getPath(), rootElement);
|
||||
|
||||
try (FileInputStream xmlInputStream = new FileInputStream(xmlFile);
|
||||
FileWriter jsonWriter = new FileWriter(jsonFile)) {
|
||||
|
||||
// 创建XML流读取器
|
||||
XMLInputFactory factory = XMLInputFactory.newInstance();
|
||||
XMLStreamReader reader = factory.createXMLStreamReader(xmlInputStream);
|
||||
|
||||
// 写入JSON数组开始标记
|
||||
jsonWriter.write("[\n");
|
||||
|
||||
boolean firstObject = true;
|
||||
@ -52,6 +105,7 @@ public class Xml2AFSIMTransformation {
|
||||
StringBuilder currentText = new StringBuilder();
|
||||
String currentElement = null;
|
||||
|
||||
// 流式解析XML
|
||||
while (reader.hasNext()) {
|
||||
int event = reader.next();
|
||||
|
||||
@ -80,7 +134,7 @@ public class Xml2AFSIMTransformation {
|
||||
String text = reader.getText().trim();
|
||||
if (!text.isEmpty()) {
|
||||
currentText.append(text);
|
||||
log.debug("文本内容: {}", text);
|
||||
log.trace("文本内容: {}", text);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -112,6 +166,7 @@ public class Xml2AFSIMTransformation {
|
||||
jsonWriter.write(json);
|
||||
objectCount++;
|
||||
|
||||
// 每处理100个对象刷新一次缓冲区并记录日志
|
||||
if (objectCount % 100 == 0) {
|
||||
log.info("已处理 {} 个对象", objectCount);
|
||||
jsonWriter.flush();
|
||||
@ -125,48 +180,68 @@ public class Xml2AFSIMTransformation {
|
||||
}
|
||||
}
|
||||
|
||||
// 写入JSON数组结束标记
|
||||
jsonWriter.write("\n]");
|
||||
log.info("转换完成,共处理 {} 个对象", objectCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("转换失败", e);
|
||||
log.error("XML转JSON转换失败,文件: {}", xmlFile.getPath(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理元素的属性
|
||||
* 处理XML元素的属性
|
||||
*
|
||||
* <p>将元素的属性添加到当前对象中,属性键名为"元素名@属性名"格式</p>
|
||||
*
|
||||
* @param reader XML流读取器
|
||||
* @param elementStack 元素栈,用于跟踪当前解析路径
|
||||
* @param currentObject 当前正在构建的对象
|
||||
*/
|
||||
private void processAttributesForElement(XMLStreamReader reader, Stack<String> elementStack,
|
||||
Map<String, Object> currentObject) {
|
||||
if (reader.getAttributeCount() > 0) {
|
||||
for (int i = 0; i < reader.getAttributeCount(); i++) {
|
||||
int attributeCount = reader.getAttributeCount();
|
||||
if (attributeCount > 0) {
|
||||
log.debug("处理元素 {} 的属性,属性数量: {}", elementStack.peek(), attributeCount);
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
String attrName = reader.getAttributeLocalName(i);
|
||||
String attrValue = reader.getAttributeValue(i);
|
||||
// 默认处理:将属性添加到对象中
|
||||
// 将属性添加到对象中,格式为:元素名@属性名
|
||||
String attrKey = elementStack.peek() + "@" + attrName;
|
||||
currentObject.put(attrKey, attrValue);
|
||||
log.trace("添加属性: {} = {}", attrKey, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理元素文本内容
|
||||
* 处理XML元素的文本内容
|
||||
*
|
||||
* <p>将元素的文本内容添加到当前对象中,键名为完整的元素路径</p>
|
||||
*
|
||||
* @param elementName 元素名称
|
||||
* @param content 元素文本内容
|
||||
* @param elementStack 元素栈,用于构建完整路径
|
||||
* @param currentObject 当前正在构建的对象
|
||||
*/
|
||||
private void processElementContent(String elementName, String content,
|
||||
Stack<String> elementStack, Map<String, Object> currentObject) {
|
||||
// 默认处理:将元素内容添加到对象中
|
||||
// 构建完整的元素路径作为key
|
||||
// 构建完整的元素路径作为key:父元素.当前元素
|
||||
String elementKey = String.join(".", elementStack) + "." + elementName;
|
||||
currentObject.put(elementKey, content);
|
||||
log.debug("添加元素内容: {} = {}", elementKey, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转换为JSON字符串
|
||||
* 将Map对象转换为JSON字符串
|
||||
*
|
||||
* @param object 要转换的对象映射
|
||||
* @return JSON格式的字符串,如果对象为空则返回null
|
||||
*/
|
||||
private String convertObjectToJson(Map<String, Object> object) {
|
||||
if (object.isEmpty()) {
|
||||
log.warn("空对象,跳过输出");
|
||||
if (object == null || object.isEmpty()) {
|
||||
log.warn("空对象,跳过JSON转换");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -174,12 +249,15 @@ public class Xml2AFSIMTransformation {
|
||||
return mapToJson(object);
|
||||
} catch (Exception e) {
|
||||
log.error("对象转JSON失败", e);
|
||||
return "{}";
|
||||
return "{}"; // 返回空对象作为容错处理
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map转JSON字符串
|
||||
* 递归将Map转换为JSON字符串
|
||||
*
|
||||
* @param map 要转换的映射
|
||||
* @return JSON对象字符串
|
||||
*/
|
||||
private String mapToJson(Map<String, Object> map) {
|
||||
if (map.isEmpty()) {
|
||||
@ -204,7 +282,10 @@ public class Xml2AFSIMTransformation {
|
||||
}
|
||||
|
||||
/**
|
||||
* 值转JSON
|
||||
* 将值转换为JSON格式
|
||||
*
|
||||
* @param value 要转换的值,支持String、Number、Boolean、Map、List等类型
|
||||
* @return JSON格式的值表示
|
||||
*/
|
||||
private String valueToJson(Object value) {
|
||||
if (value == null) {
|
||||
@ -239,14 +320,21 @@ public class Xml2AFSIMTransformation {
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
// 默认处理:转换为字符串
|
||||
return "\"" + escapeJson(value.toString()) + "\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON特殊字符转义
|
||||
* JSON字符串转义处理
|
||||
*
|
||||
* <p>转义JSON中的特殊字符,包括引号、反斜杠、控制字符等</p>
|
||||
*
|
||||
* @param text 要转义的文本
|
||||
* @return 转义后的安全JSON字符串
|
||||
*/
|
||||
private String escapeJson(String text) {
|
||||
if (text == null) return "";
|
||||
|
||||
return text.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\b", "\\b")
|
||||
@ -258,6 +346,11 @@ public class Xml2AFSIMTransformation {
|
||||
|
||||
/**
|
||||
* 应用正则表达式转换规则到单个对象
|
||||
*
|
||||
* <p>遍历对象的所有键,对每个键应用正则表达式规则进行转换</p>
|
||||
*
|
||||
* @param originalMap 原始对象映射
|
||||
* @return 应用规则转换后的新对象映射
|
||||
*/
|
||||
private Map<String, Object> applyRules(Map<String, Object> originalMap) {
|
||||
Map<String, Object> transformedMap = new HashMap<>();
|
||||
@ -270,25 +363,32 @@ public class Xml2AFSIMTransformation {
|
||||
// 如果找到匹配的规则则使用新key,否则保留原key
|
||||
if (newKey != null) {
|
||||
transformedMap.put(newKey, value);
|
||||
log.debug("转换键: {} -> {}", originalKey, newKey);
|
||||
log.debug("键名转换: {} -> {}", originalKey, newKey);
|
||||
} else {
|
||||
transformedMap.put(originalKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("规则应用完成,原始键数: {}, 转换后键数: {}", originalMap.size(), transformedMap.size());
|
||||
return transformedMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找并替换匹配的正则规则
|
||||
*
|
||||
* <p>遍历所有正则规则,找到第一个匹配的规则并进行替换</p>
|
||||
*
|
||||
* @param originalKey 原始键名
|
||||
* @return 替换后的新键名,如果没有匹配规则则返回null
|
||||
*/
|
||||
private String findAndReplace(String originalKey) {
|
||||
for (Map.Entry<Pattern, String> rule : regexRules.entrySet()) {
|
||||
if (rule.getKey().matcher(originalKey).matches()) {
|
||||
return rule.getKey().matcher(originalKey).replaceAll(rule.getValue());
|
||||
String newKey = rule.getKey().matcher(originalKey).replaceAll(rule.getValue());
|
||||
log.trace("正则匹配: {} -> {} (规则: {})", originalKey, newKey, rule.getKey().pattern());
|
||||
return newKey;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user