diff --git a/CHANGELOG.md b/CHANGELOG.md index fce8d96bf..eb0db07f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,13 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.3 (2021-06-20) +# 5.7.3 (2021-06-24) ### 🐣新特性 +* 【core 】 增加Convert.toSet方法(issue#I3XFG2@Gitee) ### 🐞Bug修复 +* 【json 】 修复XML转义字符的问题(issue#I3XH09@Gitee) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java index 78de03b31..fd3abafd8 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/Convert.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -576,7 +577,7 @@ public class Convert { * @param 元素类型 * @param elementType 集合中元素类型 * @param value 被转换的值 - * @return {@link List} + * @return {@link ArrayList} * @since 4.1.20 */ @SuppressWarnings("unchecked") @@ -584,6 +585,20 @@ public class Convert { return (List) toCollection(ArrayList.class, elementType, value); } + /** + * 转换为HashSet + * + * @param 元素类型 + * @param elementType 集合中元素类型 + * @param value 被转换的值 + * @return {@link HashSet} + * @since 5.7.3 + */ + @SuppressWarnings("unchecked") + public static Set toSet(Class elementType, Object value) { + return (Set) toCollection(HashSet.class, elementType, value); + } + /** * 转换为Map * diff --git a/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Escape.java b/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Escape.java index 2c81c5684..023ab98bc 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Escape.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Escape.java @@ -1,7 +1,6 @@ package cn.hutool.core.text.escape; import cn.hutool.core.text.replacer.LookupReplacer; -import cn.hutool.core.text.replacer.ReplacerChain; /** * HTML4的ESCAPE @@ -10,16 +9,9 @@ import cn.hutool.core.text.replacer.ReplacerChain; * @author looly * */ -public class Html4Escape extends ReplacerChain { +public class Html4Escape extends XmlEscape { 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 = { // { "\u00A0", " " }, // non-breaking space { "\u00A1", "¡" }, // inverted exclamation mark @@ -317,7 +309,7 @@ public class Html4Escape extends ReplacerChain { }; public Html4Escape() { - addChain(new LookupReplacer(BASIC_ESCAPE)); + super(); addChain(new LookupReplacer(ISO8859_1_ESCAPE)); addChain(new LookupReplacer(HTML40_EXTENDED_ESCAPE)); } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Unescape.java b/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Unescape.java index 74b37e9bc..0ac03c4c9 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Unescape.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/escape/Html4Unescape.java @@ -1,7 +1,6 @@ package cn.hutool.core.text.escape; import cn.hutool.core.text.replacer.LookupReplacer; -import cn.hutool.core.text.replacer.ReplacerChain; /** * HTML4的UNESCAPE @@ -9,20 +8,15 @@ import cn.hutool.core.text.replacer.ReplacerChain; * @author looly * */ -public class Html4Unescape extends ReplacerChain { +public class Html4Unescape extends XmlUnescape { 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[][] HTML40_EXTENDED_UNESCAPE = InternalEscapeUtil.invert(Html4Escape.HTML40_EXTENDED_ESCAPE); - // issue#1118 - protected static final String[][] OTHER_UNESCAPE = new String[][]{new String[]{"'", "'"}}; public Html4Unescape() { - addChain(new LookupReplacer(BASIC_UNESCAPE)); + super(); addChain(new LookupReplacer(ISO8859_1_UNESCAPE)); addChain(new LookupReplacer(HTML40_EXTENDED_UNESCAPE)); - addChain(new LookupReplacer(OTHER_UNESCAPE)); - addChain(new NumericEntityUnescaper()); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/text/escape/XmlEscape.java b/hutool-core/src/main/java/cn/hutool/core/text/escape/XmlEscape.java new file mode 100644 index 000000000..a6d68670f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/text/escape/XmlEscape.java @@ -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特殊字符转义
+ * 见:https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents
+ * + *
+ * 	 & (ampersand) 替换为 &amp;
+ * 	 < (less than) 替换为 &lt;
+ * 	 > (greater than) 替换为 &gt;
+ * 	 " (double quote) 替换为 &quot;
+ * 	 ' (single quote / apostrophe) 替换为 &apos;
+ * 
+ * + * @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)); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/text/escape/XmlUnescape.java b/hutool-core/src/main/java/cn/hutool/core/text/escape/XmlUnescape.java new file mode 100644 index 000000000..c7321eafa --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/text/escape/XmlUnescape.java @@ -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()); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java b/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java index e0174f0ee..fc2fabe35 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java @@ -1,10 +1,10 @@ package cn.hutool.core.text.replacer; -import java.io.Serializable; - import cn.hutool.core.lang.Replacer; import cn.hutool.core.text.StrBuilder; +import java.io.Serializable; + /** * 抽象字符串替换类
* 通过实现replace方法实现局部替换逻辑 @@ -28,18 +28,18 @@ public abstract class StrReplacer implements Replacer, Serializabl @Override public CharSequence replace(CharSequence t) { final int len = t.length(); - final StrBuilder strBuillder = StrBuilder.create(len); + final StrBuilder builder = StrBuilder.create(len); int pos = 0;//当前位置 int consumed;//处理过的字符数 while (pos < len) { - consumed = replace(t, pos, strBuillder); + consumed = replace(t, pos, builder); if (0 == consumed) { //0表示未处理或替换任何字符,原样输出本字符并从下一个字符继续 - strBuillder.append(t.charAt(pos)); + builder.append(t.charAt(pos)); pos++; } pos += consumed; } - return strBuillder; + return builder; } } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java index ae7abd74b..e6d5e2fc5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java @@ -3,6 +3,8 @@ package cn.hutool.core.util; import cn.hutool.core.lang.Filter; import cn.hutool.core.text.escape.Html4Escape; import cn.hutool.core.text.escape.Html4Unescape; +import cn.hutool.core.text.escape.XmlEscape; +import cn.hutool.core.text.escape.XmlUnescape; /** * 转义和反转义工具类Escape / Unescape
@@ -24,6 +26,37 @@ public class EscapeUtil { || StrUtil.contains(NOT_ESCAPE_CHARS, c) ); + /** + * 转义XML中的特殊字符
+ *
+	 * 	 & (ampersand) 替换为 &amp;
+	 * 	 < (less than) 替换为 &lt;
+	 * 	 > (greater than) 替换为 &gt;
+	 * 	 " (double quote) 替换为 &quot;
+	 * 	 ' (single quote / apostrophe) 替换为 &apos;
+	 * 
+ * + * @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中的特殊字符 * diff --git a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java index fbb5fd3e1..706c2db9a 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/XmlUtil.java @@ -986,7 +986,7 @@ public class XmlUtil { * @since 4.0.8 */ 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 */ public static String unescape(String string) { - return EscapeUtil.unescape(string); + return EscapeUtil.unescapeHtml4(string); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java index b49dd852d..7f5c7e597 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java @@ -4,12 +4,12 @@ import org.junit.Assert; import org.junit.Test; public class EscapeUtilTest { - + @Test public void escapeHtml4Test() { String escapeHtml4 = EscapeUtil.escapeHtml4("你好"); Assert.assertEquals("<a>你好</a>", escapeHtml4); - + String result = EscapeUtil.unescapeHtml4("振荡器类型"); Assert.assertEquals("振荡器类型", result); @@ -39,9 +39,9 @@ public class EscapeUtilTest { } @Test - public void escapeSinleQuotesTest(){ + public void escapeSingleQuotesTest(){ String str = "'some text with single quotes'"; final String s = EscapeUtil.escapeHtml4(str); - Assert.assertEquals(str, s); + Assert.assertEquals("'some text with single quotes'", s); } } diff --git a/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java index 6ddd5cc27..59558f783 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/XmlUtilTest.java @@ -287,4 +287,11 @@ public class XmlUtilTest { String format = XmlUtil.toStr(xml,"GBK",true); Console.log(format); } + + @Test + public void escapeTest(){ + String a = "<>"; + final String escape = XmlUtil.escape(a); + Console.log(escape); + } } diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/Padding.java b/hutool-crypto/src/main/java/cn/hutool/crypto/Padding.java index df2be1115..4b00ad98f 100644 --- a/hutool-crypto/src/main/java/cn/hutool/crypto/Padding.java +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/Padding.java @@ -19,9 +19,24 @@ public enum Padding { * 0补码,即不满block长度时使用0填充 */ 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, + /** + * Optimal Asymmetric Encryption Padding scheme defined in PKCS1 + */ OAEPPadding, + /** + * The padding scheme described in PKCS #1, used with the RSA algorithm + */ PKCS1Padding, + /** + * The padding scheme described in RSA Laboratories, "PKCS #5: Password-Based Encryption Standard," version 1.5, November 1993. + */ PKCS5Padding, + /** + * The padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher) + */ SSL3Padding } diff --git a/hutool-json/src/main/java/cn/hutool/json/XML.java b/hutool-json/src/main/java/cn/hutool/json/XML.java index 7dee8ef99..7d7b9e967 100644 --- a/hutool-json/src/main/java/cn/hutool/json/XML.java +++ b/hutool-json/src/main/java/cn/hutool/json/XML.java @@ -331,11 +331,11 @@ public class XML { if (i > 0) { sb.append('\n'); } - sb.append(EscapeUtil.escapeHtml4(val.toString())); + sb.append(EscapeUtil.escapeXml(val.toString())); i++; } } else { - sb.append(EscapeUtil.escapeHtml4(value.toString())); + sb.append(EscapeUtil.escapeXml(value.toString())); } // 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); } @@ -392,10 +392,12 @@ public class XML { return sb.toString(); } - String string = EscapeUtil.escapeHtml4(object.toString()); + String string = EscapeUtil.escapeXml(object.toString()); return (tagName == null) ? "\"" + string + "\"" : (string.length() == 0) ? "<" + tagName + "/>" : "<" + tagName + ">" + string + ""; } + + } diff --git a/hutool-json/src/test/java/cn/hutool/json/XMLTest.java b/hutool-json/src/test/java/cn/hutool/json/XMLTest.java index 920ec3743..927808de7 100644 --- a/hutool-json/src/test/java/cn/hutool/json/XMLTest.java +++ b/hutool-json/src/test/java/cn/hutool/json/XMLTest.java @@ -14,4 +14,15 @@ public class XMLTest { Assert.assertEquals("你好<键2>test", s); } + @Test + public void escapeTest(){ + String xml = ""; + JSONObject jsonObject = XML.toJSONObject(xml); + + Assert.assertEquals("{\"a\":\"•\"}", jsonObject.toString()); + + String xml2 = XML.toXml(JSONUtil.parseObj(jsonObject)); + Assert.assertEquals(xml, xml2); + } + }