mirror of
https://gitee.com/dromara/hutool.git
synced 2025-05-08 22:58:05 +08:00
fix escape bug
This commit is contained in:
parent
a97e967ecd
commit
949c7a856e
@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
# 5.7.3 (2021-06-20)
|
# 5.7.3 (2021-06-24)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
|
* 【core 】 增加Convert.toSet方法(issue#I3XFG2@Gitee)
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
|
* 【json 】 修复XML转义字符的问题(issue#I3XH09@Gitee)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -576,7 +577,7 @@ public class Convert {
|
|||||||
* @param <T> 元素类型
|
* @param <T> 元素类型
|
||||||
* @param elementType 集合中元素类型
|
* @param elementType 集合中元素类型
|
||||||
* @param value 被转换的值
|
* @param value 被转换的值
|
||||||
* @return {@link List}
|
* @return {@link ArrayList}
|
||||||
* @since 4.1.20
|
* @since 4.1.20
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -584,6 +585,20 @@ public class Convert {
|
|||||||
return (List<T>) toCollection(ArrayList.class, elementType, value);
|
return (List<T>) toCollection(ArrayList.class, elementType, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为HashSet
|
||||||
|
*
|
||||||
|
* @param <T> 元素类型
|
||||||
|
* @param elementType 集合中元素类型
|
||||||
|
* @param value 被转换的值
|
||||||
|
* @return {@link HashSet}
|
||||||
|
* @since 5.7.3
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Set<T> toSet(Class<T> elementType, Object value) {
|
||||||
|
return (Set<T>) toCollection(HashSet.class, elementType, value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为Map
|
* 转换为Map
|
||||||
*
|
*
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cn.hutool.core.text.escape;
|
package cn.hutool.core.text.escape;
|
||||||
|
|
||||||
import cn.hutool.core.text.replacer.LookupReplacer;
|
import cn.hutool.core.text.replacer.LookupReplacer;
|
||||||
import cn.hutool.core.text.replacer.ReplacerChain;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTML4的ESCAPE
|
* HTML4的ESCAPE
|
||||||
@ -10,16 +9,9 @@ import cn.hutool.core.text.replacer.ReplacerChain;
|
|||||||
* @author looly
|
* @author looly
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Html4Escape extends ReplacerChain {
|
public class Html4Escape extends XmlEscape {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
protected static final String[][] BASIC_ESCAPE = { //
|
|
||||||
{ "\"", """ }, // " - double-quote
|
|
||||||
{ "&", "&" }, // & - ampersand
|
|
||||||
{ "<", "<" }, // < - less-than
|
|
||||||
{ ">", ">" }, // > - greater-than
|
|
||||||
};
|
|
||||||
|
|
||||||
protected static final String[][] ISO8859_1_ESCAPE = { //
|
protected static final String[][] ISO8859_1_ESCAPE = { //
|
||||||
{ "\u00A0", " " }, // non-breaking space
|
{ "\u00A0", " " }, // non-breaking space
|
||||||
{ "\u00A1", "¡" }, // inverted exclamation mark
|
{ "\u00A1", "¡" }, // inverted exclamation mark
|
||||||
@ -317,7 +309,7 @@ public class Html4Escape extends ReplacerChain {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public Html4Escape() {
|
public Html4Escape() {
|
||||||
addChain(new LookupReplacer(BASIC_ESCAPE));
|
super();
|
||||||
addChain(new LookupReplacer(ISO8859_1_ESCAPE));
|
addChain(new LookupReplacer(ISO8859_1_ESCAPE));
|
||||||
addChain(new LookupReplacer(HTML40_EXTENDED_ESCAPE));
|
addChain(new LookupReplacer(HTML40_EXTENDED_ESCAPE));
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cn.hutool.core.text.escape;
|
package cn.hutool.core.text.escape;
|
||||||
|
|
||||||
import cn.hutool.core.text.replacer.LookupReplacer;
|
import cn.hutool.core.text.replacer.LookupReplacer;
|
||||||
import cn.hutool.core.text.replacer.ReplacerChain;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTML4的UNESCAPE
|
* HTML4的UNESCAPE
|
||||||
@ -9,20 +8,15 @@ import cn.hutool.core.text.replacer.ReplacerChain;
|
|||||||
* @author looly
|
* @author looly
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Html4Unescape extends ReplacerChain {
|
public class Html4Unescape extends XmlUnescape {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
protected static final String[][] BASIC_UNESCAPE = InternalEscapeUtil.invert(Html4Escape.BASIC_ESCAPE);
|
|
||||||
protected static final String[][] ISO8859_1_UNESCAPE = InternalEscapeUtil.invert(Html4Escape.ISO8859_1_ESCAPE);
|
protected static final String[][] ISO8859_1_UNESCAPE = InternalEscapeUtil.invert(Html4Escape.ISO8859_1_ESCAPE);
|
||||||
protected static final String[][] HTML40_EXTENDED_UNESCAPE = InternalEscapeUtil.invert(Html4Escape.HTML40_EXTENDED_ESCAPE);
|
protected static final String[][] HTML40_EXTENDED_UNESCAPE = InternalEscapeUtil.invert(Html4Escape.HTML40_EXTENDED_ESCAPE);
|
||||||
// issue#1118
|
|
||||||
protected static final String[][] OTHER_UNESCAPE = new String[][]{new String[]{"'", "'"}};
|
|
||||||
|
|
||||||
public Html4Unescape() {
|
public Html4Unescape() {
|
||||||
addChain(new LookupReplacer(BASIC_UNESCAPE));
|
super();
|
||||||
addChain(new LookupReplacer(ISO8859_1_UNESCAPE));
|
addChain(new LookupReplacer(ISO8859_1_UNESCAPE));
|
||||||
addChain(new LookupReplacer(HTML40_EXTENDED_UNESCAPE));
|
addChain(new LookupReplacer(HTML40_EXTENDED_UNESCAPE));
|
||||||
addChain(new LookupReplacer(OTHER_UNESCAPE));
|
|
||||||
addChain(new NumericEntityUnescaper());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package cn.hutool.core.text.escape;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.replacer.LookupReplacer;
|
||||||
|
import cn.hutool.core.text.replacer.ReplacerChain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML特殊字符转义<br>
|
||||||
|
* 见:https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents<br>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* & (ampersand) 替换为 &amp;
|
||||||
|
* < (less than) 替换为 &lt;
|
||||||
|
* > (greater than) 替换为 &gt;
|
||||||
|
* " (double quote) 替换为 &quot;
|
||||||
|
* ' (single quote / apostrophe) 替换为 &apos;
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.2
|
||||||
|
*/
|
||||||
|
public class XmlEscape extends ReplacerChain {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
protected static final String[][] BASIC_ESCAPE = { //
|
||||||
|
{"'", "'"}, // " - single-quote
|
||||||
|
{"\"", """}, // " - double-quote
|
||||||
|
{"&", "&"}, // & - ampersand
|
||||||
|
{"<", "<"}, // < - less-than
|
||||||
|
{">", ">"}, // > - greater-than
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public XmlEscape() {
|
||||||
|
addChain(new LookupReplacer(BASIC_ESCAPE));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.hutool.core.text.escape;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.replacer.LookupReplacer;
|
||||||
|
import cn.hutool.core.text.replacer.ReplacerChain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML的UNESCAPE
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.2
|
||||||
|
*/
|
||||||
|
public class XmlUnescape extends ReplacerChain {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
protected static final String[][] BASIC_UNESCAPE = InternalEscapeUtil.invert(XmlEscape.BASIC_ESCAPE);
|
||||||
|
|
||||||
|
public XmlUnescape() {
|
||||||
|
addChain(new LookupReplacer(BASIC_UNESCAPE));
|
||||||
|
addChain(new NumericEntityUnescaper());
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
package cn.hutool.core.text.replacer;
|
package cn.hutool.core.text.replacer;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import cn.hutool.core.lang.Replacer;
|
import cn.hutool.core.lang.Replacer;
|
||||||
import cn.hutool.core.text.StrBuilder;
|
import cn.hutool.core.text.StrBuilder;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽象字符串替换类<br>
|
* 抽象字符串替换类<br>
|
||||||
* 通过实现replace方法实现局部替换逻辑
|
* 通过实现replace方法实现局部替换逻辑
|
||||||
@ -28,18 +28,18 @@ public abstract class StrReplacer implements Replacer<CharSequence>, Serializabl
|
|||||||
@Override
|
@Override
|
||||||
public CharSequence replace(CharSequence t) {
|
public CharSequence replace(CharSequence t) {
|
||||||
final int len = t.length();
|
final int len = t.length();
|
||||||
final StrBuilder strBuillder = StrBuilder.create(len);
|
final StrBuilder builder = StrBuilder.create(len);
|
||||||
int pos = 0;//当前位置
|
int pos = 0;//当前位置
|
||||||
int consumed;//处理过的字符数
|
int consumed;//处理过的字符数
|
||||||
while (pos < len) {
|
while (pos < len) {
|
||||||
consumed = replace(t, pos, strBuillder);
|
consumed = replace(t, pos, builder);
|
||||||
if (0 == consumed) {
|
if (0 == consumed) {
|
||||||
//0表示未处理或替换任何字符,原样输出本字符并从下一个字符继续
|
//0表示未处理或替换任何字符,原样输出本字符并从下一个字符继续
|
||||||
strBuillder.append(t.charAt(pos));
|
builder.append(t.charAt(pos));
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
pos += consumed;
|
pos += consumed;
|
||||||
}
|
}
|
||||||
return strBuillder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package cn.hutool.core.util;
|
|||||||
import cn.hutool.core.lang.Filter;
|
import cn.hutool.core.lang.Filter;
|
||||||
import cn.hutool.core.text.escape.Html4Escape;
|
import cn.hutool.core.text.escape.Html4Escape;
|
||||||
import cn.hutool.core.text.escape.Html4Unescape;
|
import cn.hutool.core.text.escape.Html4Unescape;
|
||||||
|
import cn.hutool.core.text.escape.XmlEscape;
|
||||||
|
import cn.hutool.core.text.escape.XmlUnescape;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转义和反转义工具类Escape / Unescape<br>
|
* 转义和反转义工具类Escape / Unescape<br>
|
||||||
@ -24,6 +26,37 @@ public class EscapeUtil {
|
|||||||
|| StrUtil.contains(NOT_ESCAPE_CHARS, c)
|
|| StrUtil.contains(NOT_ESCAPE_CHARS, c)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义XML中的特殊字符<br>
|
||||||
|
* <pre>
|
||||||
|
* & (ampersand) 替换为 &amp;
|
||||||
|
* < (less than) 替换为 &lt;
|
||||||
|
* > (greater than) 替换为 &gt;
|
||||||
|
* " (double quote) 替换为 &quot;
|
||||||
|
* ' (single quote / apostrophe) 替换为 &apos;
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param xml XML文本
|
||||||
|
* @return 转义后的文本
|
||||||
|
* @since 5.7.2
|
||||||
|
*/
|
||||||
|
public static String escapeXml(CharSequence xml) {
|
||||||
|
XmlEscape escape = new XmlEscape();
|
||||||
|
return escape.replace(xml).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反转义XML中的特殊字符
|
||||||
|
*
|
||||||
|
* @param xml XML文本
|
||||||
|
* @return 转义后的文本
|
||||||
|
* @since 5.7.2
|
||||||
|
*/
|
||||||
|
public static String unescapeXml(CharSequence xml) {
|
||||||
|
XmlUnescape unescape = new XmlUnescape();
|
||||||
|
return unescape.replace(xml).toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转义HTML4中的特殊字符
|
* 转义HTML4中的特殊字符
|
||||||
*
|
*
|
||||||
|
@ -986,7 +986,7 @@ public class XmlUtil {
|
|||||||
* @since 4.0.8
|
* @since 4.0.8
|
||||||
*/
|
*/
|
||||||
public static String escape(String string) {
|
public static String escape(String string) {
|
||||||
return EscapeUtil.escape(string);
|
return EscapeUtil.escapeHtml4(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -998,7 +998,7 @@ public class XmlUtil {
|
|||||||
* @since 5.0.6
|
* @since 5.0.6
|
||||||
*/
|
*/
|
||||||
public static String unescape(String string) {
|
public static String unescape(String string) {
|
||||||
return EscapeUtil.unescape(string);
|
return EscapeUtil.unescapeHtml4(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,9 +39,9 @@ public class EscapeUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void escapeSinleQuotesTest(){
|
public void escapeSingleQuotesTest(){
|
||||||
String str = "'some text with single quotes'";
|
String str = "'some text with single quotes'";
|
||||||
final String s = EscapeUtil.escapeHtml4(str);
|
final String s = EscapeUtil.escapeHtml4(str);
|
||||||
Assert.assertEquals(str, s);
|
Assert.assertEquals("'some text with single quotes'", s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,4 +287,11 @@ public class XmlUtilTest {
|
|||||||
String format = XmlUtil.toStr(xml,"GBK",true);
|
String format = XmlUtil.toStr(xml,"GBK",true);
|
||||||
Console.log(format);
|
Console.log(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void escapeTest(){
|
||||||
|
String a = "<>";
|
||||||
|
final String escape = XmlUtil.escape(a);
|
||||||
|
Console.log(escape);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,24 @@ public enum Padding {
|
|||||||
* 0补码,即不满block长度时使用0填充
|
* 0补码,即不满block长度时使用0填充
|
||||||
*/
|
*/
|
||||||
ZeroPadding,
|
ZeroPadding,
|
||||||
|
/**
|
||||||
|
* This padding for block ciphers is described in 5.2 Block Encryption Algorithms in the W3C's "XML Encryption Syntax and Processing" document.
|
||||||
|
*/
|
||||||
ISO10126Padding,
|
ISO10126Padding,
|
||||||
|
/**
|
||||||
|
* Optimal Asymmetric Encryption Padding scheme defined in PKCS1
|
||||||
|
*/
|
||||||
OAEPPadding,
|
OAEPPadding,
|
||||||
|
/**
|
||||||
|
* The padding scheme described in PKCS #1, used with the RSA algorithm
|
||||||
|
*/
|
||||||
PKCS1Padding,
|
PKCS1Padding,
|
||||||
|
/**
|
||||||
|
* The padding scheme described in RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," version 1.5, November 1993.
|
||||||
|
*/
|
||||||
PKCS5Padding,
|
PKCS5Padding,
|
||||||
|
/**
|
||||||
|
* The padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher)
|
||||||
|
*/
|
||||||
SSL3Padding
|
SSL3Padding
|
||||||
}
|
}
|
||||||
|
@ -331,11 +331,11 @@ public class XML {
|
|||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
}
|
}
|
||||||
sb.append(EscapeUtil.escapeHtml4(val.toString()));
|
sb.append(EscapeUtil.escapeXml(val.toString()));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sb.append(EscapeUtil.escapeHtml4(value.toString()));
|
sb.append(EscapeUtil.escapeXml(value.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit an array of similar keys
|
// Emit an array of similar keys
|
||||||
@ -377,7 +377,7 @@ public class XML {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object.getClass().isArray()) {
|
if (ArrayUtil.isArray(object)) {
|
||||||
object = new JSONArray(object);
|
object = new JSONArray(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,10 +392,12 @@ public class XML {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String string = EscapeUtil.escapeHtml4(object.toString());
|
String string = EscapeUtil.escapeXml(object.toString());
|
||||||
return (tagName == null) ?
|
return (tagName == null) ?
|
||||||
"\"" + string + "\"" : (string.length() == 0) ? "<" + tagName + "/>"
|
"\"" + string + "\"" : (string.length() == 0) ? "<" + tagName + "/>"
|
||||||
: "<" + tagName + ">" + string + "</" + tagName + ">";
|
: "<" + tagName + ">" + string + "</" + tagName + ">";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,15 @@ public class XMLTest {
|
|||||||
Assert.assertEquals("<aaa>你好</aaa><键2>test</键2>", s);
|
Assert.assertEquals("<aaa>你好</aaa><键2>test</键2>", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void escapeTest(){
|
||||||
|
String xml = "<a>•</a>";
|
||||||
|
JSONObject jsonObject = XML.toJSONObject(xml);
|
||||||
|
|
||||||
|
Assert.assertEquals("{\"a\":\"•\"}", jsonObject.toString());
|
||||||
|
|
||||||
|
String xml2 = XML.toXml(JSONUtil.parseObj(jsonObject));
|
||||||
|
Assert.assertEquals(xml, xml2);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user