diff --git a/liteflow-core/pom.xml b/liteflow-core/pom.xml
index 3eebab572..7a4403c32 100644
--- a/liteflow-core/pom.xml
+++ b/liteflow-core/pom.xml
@@ -63,5 +63,9 @@
commons-io
commons-io
+
+ org.apache.commons
+ commons-text
+
diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/SelectiveJavaEscaper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/SelectiveJavaEscaper.java
new file mode 100644
index 000000000..f7b1eaeef
--- /dev/null
+++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/SelectiveJavaEscaper.java
@@ -0,0 +1,108 @@
+package com.yomahub.liteflow.util;
+
+import org.apache.commons.text.translate.AggregateTranslator;
+import org.apache.commons.text.translate.CharSequenceTranslator;
+import org.apache.commons.text.translate.LookupTranslator;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 提供选择性的Java字符串转义功能,仅转义必要字符,保留非ASCII字符。
+ * @author Bryan.Zhang
+ * @since 2.13.2
+ */
+public final class SelectiveJavaEscaper {
+
+ // 私有构造函数,防止实例化工具类
+ private SelectiveJavaEscaper() {
+ throw new UnsupportedOperationException("Utility class should not be instantiated");
+ }
+
+ /**
+ * 自定义的CharSequenceTranslator,用于选择性Java转义。
+ * 它转义:", \, 和标准的Java控制字符 (\n, \t, \r, \f, \b)。
+ * 它保留所有其他字符,包括非ASCII字符。
+ */
+ public static final CharSequenceTranslator ESCAPE_JAVA_SELECTIVE;
+
+ static {
+ // 1. 定义基础转义映射 (引号和反斜杠)
+ final Map basicEscapeMap = new HashMap<>();
+ basicEscapeMap.put("\"", "\\\"");
+ basicEscapeMap.put("\\", "\\\\");
+ final CharSequenceTranslator basicEscaper = new LookupTranslator(Collections.unmodifiableMap(basicEscapeMap));
+
+ // 2. 定义控制字符转义映射
+ // 注意: 实际项目中应验证EntityArrays或手动确保映射完整
+ final Map controlCharsMap = new HashMap<>();
+ controlCharsMap.put("\n", "\\n");
+ controlCharsMap.put("\t", "\\t");
+ controlCharsMap.put("\r", "\\r");
+ controlCharsMap.put("\f", "\\f");
+ controlCharsMap.put("\b", "\\b");
+ // 可以在这里添加其他需要的控制字符转义,例如垂直制表符\v (\u000B -> \\v 并不标准,通常转为\u000B)
+ // 对于Java,通常只需要处理上述5个以及可能的其他ASCII控制码(< 32)
+ final CharSequenceTranslator controlCharsEscaper = new LookupTranslator(Collections.unmodifiableMap(controlCharsMap));
+
+ // 3. 使用AggregateTranslator组合转换器
+ // 顺序可能影响结果,这里假设AggregateTranslator会正确处理
+ // 通常将最具体的或最需要优先处理的放在前面可能更安全
+ ESCAPE_JAVA_SELECTIVE = new AggregateTranslator(
+ controlCharsEscaper, // 先处理 \n, \t 等
+ basicEscaper // 再处理 " 和 \
+ );
+ // 关键:没有添加任何形式的 UnicodeEscaper
+ }
+
+ /**
+ * 对输入字符串应用选择性Java转义规则。
+ *
+ * @param input 要转义的字符串,可以为 null
+ * @return 转义后的字符串;如果输入为 null,则返回 null
+ */
+ public static String escape(final String input) {
+ if (input == null) {
+ return null;
+ }
+ // StringWriter 用于高效构建结果字符串
+ // 初始容量设为输入长度的1.5倍,是一个合理的估计
+ StringWriter writer = new StringWriter((int) (input.length() * 1.5));
+ try {
+ // 执行翻译
+ ESCAPE_JAVA_SELECTIVE.translate(input, writer);
+ } catch (IOException ioe) {
+ // 根据 StringWriter 的 Javadoc,它的 append 方法不会抛出 IOException
+ // 但 translate 方法声明了可能抛出,为严谨起见进行包装
+ throw new AssertionError("IOException thrown by StringWriter", ioe);
+ }
+ return writer.toString();
+ }
+
+ /**
+ * 主方法,用于演示转义效果。
+ * @param args 命令行参数(未使用)
+ */
+ public static void main(String[] args) {
+ String originalString = "你好,\"世界\"!\n\tEnd.";
+ System.out.println("原始字符串: " + originalString);
+
+ String escapedString = SelectiveJavaEscaper.escape(originalString);
+ System.out.println("选择性转义后: " + escapedString);
+
+ // 预期输出: 你好,\"世界\"!\\n\\tEnd.
+ // 验证: 中文字符被保留,引号、反斜杠、换行符、制表符被正确转义
+
+ // 对比(可选):使用标准 escapeJava
+ // 需要导入 org.apache.commons.text.StringEscapeUtils
+ // try {
+ // String defaultEscaped = org.apache.commons.text.StringEscapeUtils.escapeJava(originalString);
+ // System.out.println("标准 escapeJava: " + defaultEscaped);
+ // // 预期输出: \u4F60\u597D\uFF0C\"世界\"!\n\tEnd. (你好和,被转义)
+ // } catch (NoClassDefFoundError e) {
+ // System.out.println("标准 escapeJava 未执行 (可能未取消注释或依赖问题)");
+ // }
+ }
+}
diff --git a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java
index 419351a62..aa2451bc5 100644
--- a/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java
+++ b/liteflow-el-builder/src/main/java/com/yomahub/liteflow/builder/el/ELWrapper.java
@@ -4,7 +4,7 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.builder.el.vo.RetryELVo;
import com.yomahub.liteflow.util.JsonUtil;
-import org.apache.commons.lang.StringEscapeUtils;
+import com.yomahub.liteflow.util.SelectiveJavaEscaper;
import java.util.*;
@@ -142,7 +142,7 @@ public abstract class ELWrapper {
* @return {@link ELWrapper}
*/
protected ELWrapper data(String dataName, Object object){
- setData(StrUtil.format("\"{}\"",StringEscapeUtils.escapeJava(JsonUtil.toJsonString(object))));
+ setData(StrUtil.format("\"{}\"", SelectiveJavaEscaper.escape(JsonUtil.toJsonString(object))));
setDataName(dataName);
return this;
}
@@ -155,7 +155,7 @@ public abstract class ELWrapper {
* @return {@link ELWrapper}
*/
protected ELWrapper data(String dataName, String jsonString){
- setData(StrUtil.format("\"{}\"",StringEscapeUtils.escapeJava(jsonString)));
+ setData(StrUtil.format("\"{}\"",SelectiveJavaEscaper.escape(jsonString)));
setDataName(dataName);
return this;
}
@@ -168,7 +168,7 @@ public abstract class ELWrapper {
* @return {@link ELWrapper}
*/
protected ELWrapper data(String dataName, Map jsonMap){
- setData(StrUtil.format("\"{}\"", StringEscapeUtils.escapeJava(JsonUtil.toJsonString(jsonMap))));
+ setData(StrUtil.format("\"{}\"", SelectiveJavaEscaper.escape(JsonUtil.toJsonString(jsonMap))));
setDataName(dataName);
return this;
}
@@ -242,14 +242,14 @@ public abstract class ELWrapper {
elContext.append(StrUtil.format(".id(\"{}\")", this.getId()));
}
if(this.getTag() != null){
- elContext.append(StrUtil.format(".tag(\"{}\")", StringEscapeUtils.escapeJava(this.getTag())));
+ elContext.append(StrUtil.format(".tag(\"{}\")", SelectiveJavaEscaper.escape(this.getTag())));
}
if(this.getData() != null){
elContext.append(StrUtil.format(".data({})", this.getDataName()));
paramContext.append(StrUtil.format("{} = {}", this.getDataName(), this.getData())).append(";\n");
}
if(MapUtil.isNotEmpty(this.getBindData())){
- this.getBindData().forEach((key, value) -> elContext.append(StrUtil.format(".bind(\"{}\", \"{}\")", key, StringEscapeUtils.escapeJava(value))));
+ this.getBindData().forEach((key, value) -> elContext.append(StrUtil.format(".bind(\"{}\", \"{}\")", key, SelectiveJavaEscaper.escape(value))));
}
if(this.getMaxWaitSeconds() != null){
elContext.append(StrUtil.format(".maxWaitSeconds({})", String.valueOf(this.getMaxWaitSeconds())));