mirror of https://gitee.com/dromara/liteFlow
bug #IC0254 node在设置data数据时,含有中文时会解析成Unicode转义字符
This commit is contained in:
parent
6de59cb3dd
commit
97af056acf
|
@ -63,5 +63,9 @@
|
|||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -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<CharSequence, CharSequence> basicEscapeMap = new HashMap<>();
|
||||
basicEscapeMap.put("\"", "\\\"");
|
||||
basicEscapeMap.put("\\", "\\\\");
|
||||
final CharSequenceTranslator basicEscaper = new LookupTranslator(Collections.unmodifiableMap(basicEscapeMap));
|
||||
|
||||
// 2. 定义控制字符转义映射
|
||||
// 注意: 实际项目中应验证EntityArrays或手动确保映射完整
|
||||
final Map<CharSequence, CharSequence> 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 未执行 (可能未取消注释或依赖问题)");
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -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<String, Object> 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())));
|
||||
|
|
Loading…
Reference in New Issue