diff --git a/src/main/java/com/github/underscore/Xml.java b/src/main/java/com/github/underscore/Xml.java index 793da57c..651fd917 100644 --- a/src/main/java/com/github/underscore/Xml.java +++ b/src/main/java/com/github/underscore/Xml.java @@ -1045,9 +1045,9 @@ private static void escape(String s, StringBuilder sb) { || ch >= '\u007F' && ch <= '\u009F' || ch >= '\u2000' && ch <= '\u20FF') { String ss = Integer.toHexString(ch); - sb.append("&#x"); + sb.append("\\x"); sb.append("0".repeat(4 - ss.length())); - sb.append(ss.toUpperCase()).append(";"); + sb.append(ss.toUpperCase()); } else { sb.append(ch); } @@ -1101,7 +1101,28 @@ private static int translate( } } } - return 0; + return translateBinary(input, index, builder); + } + + static int translateBinary( + final CharSequence input, final int index, final StringBuilder builder) { + final int length = input.length(); + if (index + 5 >= length) { + return 0; + } + if (input.charAt(index) != '\\' || input.charAt(index + 1) != 'x') { + return 0; + } + int value = 0; + for (int i = index + 2; i < index + 6; i++) { + final int digit = Character.digit(input.charAt(i), 16); + if (digit < 0) { + return 0; + } + value = (value << 4) | digit; + } + builder.append((char) value); + return 6; } public static String getMapKey(Object map) { @@ -1283,7 +1304,8 @@ private static Object getValue(final String name, final Object value, final From } else { localValue = value; } - return localValue instanceof String && name.startsWith("-") + return localValue instanceof String + && (name.startsWith("-") || ((String) localValue).contains("\\x")) ? XmlValue.unescape((String) localValue) : localValue; } diff --git a/src/test/java/com/github/underscore/LodashTest.java b/src/test/java/com/github/underscore/LodashTest.java index bf1fbe9b..1b08d81c 100644 --- a/src/test/java/com/github/underscore/LodashTest.java +++ b/src/test/java/com/github/underscore/LodashTest.java @@ -1152,6 +1152,14 @@ void xmpToJson6() { + "")); } + @Test + void xmlToJson7() { + assertEquals( + "{\n \"debug\": \"\\u0001\"\n}", + U.xmlToJson( + "\n\\x0001")); + } + @Test void xmlToJsonMinimum() { assertEquals( diff --git a/src/test/java/com/github/underscore/StringTest.java b/src/test/java/com/github/underscore/StringTest.java index 88458ed6..e066e0e2 100644 --- a/src/test/java/com/github/underscore/StringTest.java +++ b/src/test/java/com/github/underscore/StringTest.java @@ -1197,14 +1197,14 @@ void escapeXml() { assertEquals(" ", Xml.XmlValue.escape("\r")); assertEquals("\t", Xml.XmlValue.escape("\t")); assertEquals("/", Xml.XmlValue.escape("/")); - assertEquals("�", Xml.XmlValue.escape("\u0000")); - assertEquals("", Xml.XmlValue.escape("\u001F")); + assertEquals("\\x0000", Xml.XmlValue.escape("\u0000")); + assertEquals("\\x001F", Xml.XmlValue.escape("\u001F")); assertEquals("\u0020", Xml.XmlValue.escape("\u0020")); - assertEquals("", Xml.XmlValue.escape("\u007F")); - assertEquals("Ÿ", Xml.XmlValue.escape("\u009F")); + assertEquals("\\x007F", Xml.XmlValue.escape("\u007F")); + assertEquals("\\x009F", Xml.XmlValue.escape("\u009F")); assertEquals("\u00A0", Xml.XmlValue.escape("\u00A0")); - assertEquals(" ", Xml.XmlValue.escape("\u2000")); - assertEquals("⃿", Xml.XmlValue.escape("\u20FF")); + assertEquals("\\x2000", Xml.XmlValue.escape("\u2000")); + assertEquals("\\x20FF", Xml.XmlValue.escape("\u20FF")); assertEquals("\u2100", Xml.XmlValue.escape("\u2100")); assertEquals("\uFFFF", Xml.XmlValue.escape("\uFFFF")); } @@ -1219,6 +1219,96 @@ void unescapeXml() { assertEquals("<", Xml.XmlValue.unescape("<")); assertEquals(">", Xml.XmlValue.unescape(">")); assertEquals(""", Xml.XmlValue.unescape(""")); + assertEquals("\u20FF", Xml.XmlValue.unescape("\\x20FF")); + assertEquals("\u0000", Xml.XmlValue.unescape("\\x0000")); + assertEquals("\u001F", Xml.XmlValue.unescape("\\x001F")); + } + + @Test + void translateBinary() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\x0041", 0, builder); + assertEquals(6, result); + assertEquals("A", builder.toString()); + } + + @Test + void translateBinary2() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("a\\x0041", 1, builder); + assertEquals(6, result); + assertEquals("A", builder.toString()); + } + + @Test + void translateBinary3() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("abc", 0, builder); + assertEquals(0, result); + assertEquals("", builder.toString()); + } + + @Test + void translateBinary4() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\u0041", 0, builder); + assertEquals(0, result); + assertEquals("", builder.toString()); + } + + @Test + void translateBinary5() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\x00G1", 0, builder); + assertEquals(0, result); + assertEquals("", builder.toString()); + } + + @Test + void translateBinary6() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\x041", 0, builder); + assertEquals(0, result); + assertEquals("", builder.toString()); + } + + @Test + void translateBinary7() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\x0000", 0, builder); + assertEquals(6, result); + assertEquals(1, builder.length()); + assertEquals(0, builder.charAt(0)); + } + + @SuppressWarnings("java:S5976") + @Test + void translateBinary8() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\x1234", 0, builder); + assertEquals(6, result); + assertEquals(1, builder.length()); + assertEquals('\u1234', builder.charAt(0)); + } + + @SuppressWarnings("java:S5976") + @Test + void translateBinary9() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\xFFFF", 0, builder); + assertEquals(6, result); + assertEquals(1, builder.length()); + assertEquals('\uFFFF', builder.charAt(0)); + } + + @SuppressWarnings("java:S5976") + @Test + void translateBinary10() { + StringBuilder builder = new StringBuilder(); + int result = Xml.XmlValue.translateBinary("\\x00af", 0, builder); + assertEquals(6, result); + assertEquals(1, builder.length()); + assertEquals('\u00AF', builder.charAt(0)); } @Test @@ -1510,6 +1600,16 @@ void testXmlDecodeCyrillic() { U.toXml((List) U.fromJson("[\"Текст на русском\"]"))); } + @SuppressWarnings("unchecked") + @Test + void testXmlDecodeBinary() { + assertEquals( + "\n\n " + + "\\x0001Текст на русском\n" + + "", + U.toXml((List) U.fromJson("[\"\\u0001Текст на русском\"]"))); + } + @Test void toXmlFromList() { final List testList = new ArrayList<>();