From 0fec19a20dc223e2027e4b3a58f2a1d9370396bc Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Thu, 4 Jun 2026 14:22:23 +0300 Subject: [PATCH 1/5] Improved read XML attributes --- src/main/java/com/github/underscore/Xml.java | 21 ++++++++++++------- .../com/github/underscore/LodashTest.java | 15 +++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/github/underscore/Xml.java b/src/main/java/com/github/underscore/Xml.java index 793da57c..ab9bbb01 100644 --- a/src/main/java/com/github/underscore/Xml.java +++ b/src/main/java/com/github/underscore/Xml.java @@ -1521,15 +1521,22 @@ static Map parseAttributes(final String source) { StringBuilder key = new StringBuilder(); StringBuilder value = new StringBuilder(); boolean inQuotes = false; + char quoteChar = 0; boolean expectingValue = false; for (char c : source.toCharArray()) { - if (c == '"') { - inQuotes = !inQuotes; - if (!inQuotes && expectingValue) { - result.put(key.toString(), value.toString()); - key.setLength(0); - value.setLength(0); - expectingValue = false; + if ((c == '"' || c == '\'') && (!inQuotes || c == quoteChar)) { + if (!inQuotes) { + inQuotes = true; + quoteChar = c; + } else { + inQuotes = false; + quoteChar = 0; + if (expectingValue) { + result.put(key.toString(), value.toString()); + key.setLength(0); + value.setLength(0); + expectingValue = false; + } } } else if (c == '=' && !inQuotes) { expectingValue = true; diff --git a/src/test/java/com/github/underscore/LodashTest.java b/src/test/java/com/github/underscore/LodashTest.java index bf1fbe9b..1a6416f4 100644 --- a/src/test/java/com/github/underscore/LodashTest.java +++ b/src/test/java/com/github/underscore/LodashTest.java @@ -1152,6 +1152,21 @@ void xmpToJson6() { + "")); } + @Test + void xmpToJson7() { + assertEquals( + "{\n" + + " \"Comment\": {\n" + + " \"-stringValue\": \"a\",\n" + + " \"-self-closing\": \"true\"\n" + + " },\n" + + " \"#omit-xml-declaration\": \"yes\"\n" + + "}", + U.xmlToJson("")); + assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); + assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); - assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); - assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); + assertEquals( + "{\n" + + " \"Comment\": {\n" + + " \"-stringValue\": \"a'\",\n" + + " \"-self-closing\": \"true\"\n" + + " },\n" + + " \"#omit-xml-declaration\": \"yes\"\n" + + "}", + U.xmlToJson("")); + assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); + assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); - assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); - assertThrows(IllegalArgumentException.class, () -> U.xmlToJson("")); + assertThrows( + IllegalArgumentException.class, () -> U.xmlToJson("")); } + @ParameterizedTest(name = "{0}") + @CsvSource(delimiter = '|', value = { + // input | expected (k=v,k=v) + "key=\"value\" | key=value", + "key='value' | key=value", + "a=\"1\" b='2' | a=1,b=2", + "key=\"it's a value\" | key=it's a value", + "key='say \"hi\"' | key=say \"hi\"", + "key=\"a=b=c\" | key=a=b=c", + "key=\"\" | key=", + " key =\"value\" | key=value", + "data-id=\"5\" | data-id=5", + "x==\"y\" | x=y", + "k=\"first\" k=\"second\" | k=second", + }) + void parses(String input, String expected) { + assertEquals(parse(expected), Xml.parseAttributes(input)); + } + + @ParameterizedTest(name = "empty: \"{0}\"") + @CsvSource({ + "''", + "\"orphan\"", + "lonekey", + "key=\"value", + }) + void producesNothing(String input) { + assertTrue(Xml.parseAttributes(input).isEmpty()); + } + + @Test + void preservesInsertionOrder() { + assertEquals("[z, a, m]", + Xml.parseAttributes("z=\"1\" a=\"2\" m=\"3\"").keySet().toString()); + } + + // builds expected map from "k=v,k=v" + private static Map parse(String s) { + Map m = new LinkedHashMap<>(); + if (s != null && !s.isEmpty()) { + for (String pair : s.split(",", -1)) { + int i = pair.indexOf('='); + m.put(pair.substring(0, i), pair.substring(i + 1)); + } + } + return m; + } + @Test void xmlToJsonMinimum() { assertEquals( From c1336d973c3d1976fc8c1d7f6a8a11742acb22e7 Mon Sep 17 00:00:00 2001 From: Valentyn Kolesnikov Date: Thu, 4 Jun 2026 15:59:42 +0300 Subject: [PATCH 5/5] Added new dependency --- pom-central.xml | 6 ++++++ pom-pack.xml | 6 ++++++ pom.xml | 12 ++++++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/pom-central.xml b/pom-central.xml index 38684af8..ed568d42 100644 --- a/pom-central.xml +++ b/pom-central.xml @@ -165,6 +165,12 @@ 5.14.4 test + + org.junit.jupiter + junit-jupiter-params + 5.14.4 + test + org.junit.platform junit-platform-launcher diff --git a/pom-pack.xml b/pom-pack.xml index a5784506..cb8f1b33 100644 --- a/pom-pack.xml +++ b/pom-pack.xml @@ -253,6 +253,12 @@ 5.14.4 test + + org.junit.jupiter + junit-jupiter-params + 5.14.4 + test + org.junit.platform junit-platform-launcher diff --git a/pom.xml b/pom.xml index 6635ffbe..56aee75d 100644 --- a/pom.xml +++ b/pom.xml @@ -210,18 +210,18 @@ 5.14.4 test - - org.junit.platform - junit-platform-launcher - 1.14.4 - test - org.junit.jupiter junit-jupiter-params 5.14.4 test + + org.junit.platform + junit-platform-launcher + 1.14.4 + test + org.awaitility awaitility