Skip to content

Commit

Permalink
Improved attribute parser to handle values with equal signs
Browse files Browse the repository at this point in the history
  • Loading branch information
javadev authored Dec 11, 2024
1 parent 215d2fd commit 85b7bcc
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 22 deletions.
36 changes: 16 additions & 20 deletions src/main/java/com/github/underscore/Xml.java
Original file line number Diff line number Diff line change
Expand Up @@ -1521,30 +1521,26 @@ private static Object addElement(
}

static Map<String, String> parseAttributes(final String source) {
final Map<String, String> result = new LinkedHashMap<>();
final StringBuilder key = new StringBuilder();
final StringBuilder value = new StringBuilder();
boolean quoteFound = false;
boolean equalFound = false;
for (int index = 0; index < source.length(); index += 1) {
if (source.charAt(index) == '=') {
equalFound = !equalFound;
continue;
}
if (source.charAt(index) == '"') {
if (quoteFound && equalFound) {
Map<String, String> result = new LinkedHashMap<>();
StringBuilder key = new StringBuilder();
StringBuilder value = new StringBuilder();
boolean inQuotes = false;
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);
equalFound = false;
}
quoteFound = !quoteFound;
} else if (quoteFound || SKIPPED_CHARS.contains(source.charAt(index))) {
if (quoteFound) {
value.append(source.charAt(index));
expectingValue = false;
}
} else {
key.append(source.charAt(index));
} else if (c == '=' && !inQuotes) {
expectingValue = true;
} else if (inQuotes) {
value.append(c);
} else if (!SKIPPED_CHARS.contains(c)) {
key.append(c);
}
}
return result;
Expand Down
13 changes: 13 additions & 0 deletions src/test/java/com/github/underscore/LodashTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,19 @@ void xmpToJson4() {
+ "</z:catalog>"));
}

@Test
void xmpToJson5() {
assertEquals("{\n"
+ " \"Comment\": {\n"
+ " \"-stringValue\": \"============================\",\n"
+ " \"-self-closing\": \"true\"\n"
+ " },\n"
+ " \"#omit-xml-declaration\": \"yes\"\n"
+ "}",
U.xmlToJson(
"<Comment stringValue=\"============================\"/>"));
}

@Test
void xmlToJsonMinimum() {
assertEquals(
Expand Down
66 changes: 64 additions & 2 deletions src/test/java/com/github/underscore/StringTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2358,9 +2358,71 @@ void parseAttributes() {
"{version=1.0}",
Xml.parseAttributes(" version = \"1.0\" encoding= \"UTF-8 ").toString());
assertEquals(
"{}", Xml.parseAttributes(" version = \"1.0 encoding= \"UTF-8\" ").toString());
"{version=1.0 encoding= }",
Xml.parseAttributes(" version = \"1.0 encoding= \"UTF-8\" ").toString());
assertEquals(
"{}", Xml.parseAttributes(" version = 1.0\" encoding= \"UTF-8\" ").toString());
"{version1.0= encoding= }",
Xml.parseAttributes(" version = 1.0\" encoding= \"UTF-8\" ").toString());
}

@Test
void testSingleAttribute() {
Map<String, String> result = Xml.parseAttributes("key1=\"value1\"");
assertEquals(Map.of("key1", "value1"), result);
}

@Test
void testMultipleAttributes() {
Map<String, String> result = Xml.parseAttributes("key1=\"value1\" key2=\"value2\"");
assertEquals(Map.of("key1", "value1", "key2", "value2"), result);
}

@Test
void testAttributeWithSpaces() {
Map<String, String> result = Xml.parseAttributes("key1=\"value with spaces\" key2=\"another value\"");
assertEquals(Map.of("key1", "value with spaces", "key2", "another value"), result);
}

@Test
void testEmptyValue() {
Map<String, String> result = Xml.parseAttributes("key1=\"value1\" key2=\"\"");
assertEquals(Map.of("key1", "value1", "key2", ""), result);
}

@Test
void testAttributesWithoutSpaceSeparation() {
Map<String, String> result = Xml.parseAttributes("key1=\"value1\"key2=\"value2\"");
assertEquals(Map.of("key1", "value1", "key2", "value2"), result);
}

@Test
void testUnclosedQuotes() {
Map<String, String> result = Xml.parseAttributes("key1=\"value1 key2=\"value2\"");
assertEquals(Map.of("key1", "value1 key2="), result);
}

@Test
void testEqualsSignInValue() {
Map<String, String> result = Xml.parseAttributes("key1=\"value=1\" key2=\"value=2\"");
assertEquals(Map.of("key1", "value=1", "key2", "value=2"), result);
}

@Test
void testTrailingWhitespace() {
Map<String, String> result = Xml.parseAttributes("key1=\"value1\" key2=\"value2\" ");
assertEquals(Map.of("key1", "value1", "key2", "value2"), result);
}

@Test
void testLeadingWhitespace() {
Map<String, String> result = Xml.parseAttributes(" key1=\"value1\" key2=\"value2\"");
assertEquals(Map.of("key1", "value1", "key2", "value2"), result);
}

@Test
void testNoEqualsSign() {
Map<String, String> result = Xml.parseAttributes("key1\"value1\" key2=\"value2\"");
assertEquals(Map.of("key1key2", "value1value2"), result);
}

@SuppressWarnings("unchecked")
Expand Down

0 comments on commit 85b7bcc

Please sign in to comment.