From 5f8ea0094f26d64a134ec7d072fa7f3aa6013f62 Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Sat, 9 May 2026 09:56:56 +0300 Subject: [PATCH 1/6] Improved binary in xml strings --- src/main/java/com/github/underscore/Xml.java | 25 ++++- .../com/github/underscore/StringTest.java | 99 +++++++++++++++++-- 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/underscore/Xml.java b/src/main/java/com/github/underscore/Xml.java index 793da57c..6a13007a 100644 --- a/src/main/java/com/github/underscore/Xml.java +++ b/src/main/java/com/github/underscore/Xml.java @@ -1045,7 +1045,7 @@ 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(";"); } else { @@ -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) { diff --git a/src/test/java/com/github/underscore/StringTest.java b/src/test/java/com/github/underscore/StringTest.java index 88458ed6..492fe674 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,93 @@ 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)); + } + + @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)); + } + + @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)); + } + + @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 From 7475148bc406e1db797e016ae011b8acdf9beee9 Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Sat, 9 May 2026 10:06:53 +0300 Subject: [PATCH 2/6] Fixed sonar --- src/test/java/com/github/underscore/StringTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/com/github/underscore/StringTest.java b/src/test/java/com/github/underscore/StringTest.java index 492fe674..a45e4b7e 100644 --- a/src/test/java/com/github/underscore/StringTest.java +++ b/src/test/java/com/github/underscore/StringTest.java @@ -1281,6 +1281,7 @@ void translateBinary7() { assertEquals(0, builder.charAt(0)); } + @SuppressWarnings("java:S5976") @Test void translateBinary8() { StringBuilder builder = new StringBuilder(); @@ -1290,6 +1291,7 @@ void translateBinary8() { assertEquals('\u1234', builder.charAt(0)); } + @SuppressWarnings("java:S5976") @Test void translateBinary9() { StringBuilder builder = new StringBuilder(); @@ -1299,6 +1301,7 @@ void translateBinary9() { assertEquals('\uFFFF', builder.charAt(0)); } + @SuppressWarnings("java:S5976") @Test void translateBinary10() { StringBuilder builder = new StringBuilder(); From a221fda7611a0a9e9411de5f24ace9f0458f9494 Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Sat, 9 May 2026 11:06:22 +0300 Subject: [PATCH 3/6] Improved unescape --- src/main/java/com/github/underscore/Xml.java | 4 ++-- src/test/java/com/github/underscore/StringTest.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/underscore/Xml.java b/src/main/java/com/github/underscore/Xml.java index 6a13007a..7f7cb377 100644 --- a/src/main/java/com/github/underscore/Xml.java +++ b/src/main/java/com/github/underscore/Xml.java @@ -1047,7 +1047,7 @@ private static void escape(String s, StringBuilder sb) { String ss = Integer.toHexString(ch); sb.append("\\x"); sb.append("0".repeat(4 - ss.length())); - sb.append(ss.toUpperCase()).append(";"); + sb.append(ss.toUpperCase()); } else { sb.append(ch); } @@ -1304,7 +1304,7 @@ 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/StringTest.java b/src/test/java/com/github/underscore/StringTest.java index a45e4b7e..7e0383eb 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("\\x0000;", Xml.XmlValue.escape("\u0000")); - assertEquals("\\x001F;", Xml.XmlValue.escape("\u001F")); + assertEquals("\\x0000", Xml.XmlValue.escape("\u0000")); + assertEquals("\\x001F", Xml.XmlValue.escape("\u001F")); assertEquals("\u0020", Xml.XmlValue.escape("\u0020")); - assertEquals("\\x007F;", Xml.XmlValue.escape("\u007F")); - assertEquals("\\x009F;", Xml.XmlValue.escape("\u009F")); + assertEquals("\\x007F", Xml.XmlValue.escape("\u007F")); + assertEquals("\\x009F", Xml.XmlValue.escape("\u009F")); assertEquals("\u00A0", Xml.XmlValue.escape("\u00A0")); - assertEquals("\\x2000;", Xml.XmlValue.escape("\u2000")); - assertEquals("\\x20FF;", 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")); } From 04b0e269544d88eef5dd254425f803867c000d54 Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Sat, 9 May 2026 11:17:57 +0300 Subject: [PATCH 4/6] Added decode binary test --- src/test/java/com/github/underscore/StringTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/github/underscore/StringTest.java b/src/test/java/com/github/underscore/StringTest.java index 7e0383eb..e066e0e2 100644 --- a/src/test/java/com/github/underscore/StringTest.java +++ b/src/test/java/com/github/underscore/StringTest.java @@ -1600,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<>(); From 264d18033b4af618a7c2601f11d2fca460f0e381 Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Sat, 9 May 2026 12:59:37 +0300 Subject: [PATCH 5/6] Restored code --- src/main/java/com/github/underscore/Xml.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/underscore/Xml.java b/src/main/java/com/github/underscore/Xml.java index 7f7cb377..0c677af2 100644 --- a/src/main/java/com/github/underscore/Xml.java +++ b/src/main/java/com/github/underscore/Xml.java @@ -1304,7 +1304,7 @@ private static Object getValue(final String name, final Object value, final From } else { localValue = value; } - return localValue instanceof String && (name.startsWith("-") || ((String) localValue).contains("\\x")) + return localValue instanceof String && name.startsWith("-") ? XmlValue.unescape((String) localValue) : localValue; } From b04292ba4b62615ed565c835c29ec513f045303c Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Sat, 9 May 2026 16:20:13 +0300 Subject: [PATCH 6/6] Added test --- src/main/java/com/github/underscore/Xml.java | 3 ++- src/test/java/com/github/underscore/LodashTest.java | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/underscore/Xml.java b/src/main/java/com/github/underscore/Xml.java index 0c677af2..651fd917 100644 --- a/src/main/java/com/github/underscore/Xml.java +++ b/src/main/java/com/github/underscore/Xml.java @@ -1304,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(