Skip to content

Commit a054dbb

Browse files
authored
Improve reading of XML attributes
1 parent de9d010 commit a054dbb

5 files changed

Lines changed: 115 additions & 7 deletions

File tree

pom-central.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@
165165
<version>5.14.4</version>
166166
<scope>test</scope>
167167
</dependency>
168+
<dependency>
169+
<groupId>org.junit.jupiter</groupId>
170+
<artifactId>junit-jupiter-params</artifactId>
171+
<version>5.14.4</version>
172+
<scope>test</scope>
173+
</dependency>
168174
<dependency>
169175
<groupId>org.junit.platform</groupId>
170176
<artifactId>junit-platform-launcher</artifactId>

pom-pack.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@
253253
<version>5.14.4</version>
254254
<scope>test</scope>
255255
</dependency>
256+
<dependency>
257+
<groupId>org.junit.jupiter</groupId>
258+
<artifactId>junit-jupiter-params</artifactId>
259+
<version>5.14.4</version>
260+
<scope>test</scope>
261+
</dependency>
256262
<dependency>
257263
<groupId>org.junit.platform</groupId>
258264
<artifactId>junit-platform-launcher</artifactId>

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@
210210
<version>5.14.4</version>
211211
<scope>test</scope>
212212
</dependency>
213+
<dependency>
214+
<groupId>org.junit.jupiter</groupId>
215+
<artifactId>junit-jupiter-params</artifactId>
216+
<version>5.14.4</version>
217+
<scope>test</scope>
218+
</dependency>
213219
<dependency>
214220
<groupId>org.junit.platform</groupId>
215221
<artifactId>junit-platform-launcher</artifactId>

src/main/java/com/github/underscore/Xml.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,15 +1521,22 @@ static Map<String, String> parseAttributes(final String source) {
15211521
StringBuilder key = new StringBuilder();
15221522
StringBuilder value = new StringBuilder();
15231523
boolean inQuotes = false;
1524+
char quoteChar = 0;
15241525
boolean expectingValue = false;
15251526
for (char c : source.toCharArray()) {
1526-
if (c == '"') {
1527-
inQuotes = !inQuotes;
1528-
if (!inQuotes && expectingValue) {
1529-
result.put(key.toString(), value.toString());
1530-
key.setLength(0);
1531-
value.setLength(0);
1532-
expectingValue = false;
1527+
if ((c == '"' || c == '\'') && (!inQuotes || c == quoteChar)) {
1528+
if (!inQuotes) {
1529+
inQuotes = true;
1530+
quoteChar = c;
1531+
} else {
1532+
inQuotes = false;
1533+
quoteChar = 0;
1534+
if (expectingValue) {
1535+
result.put(key.toString(), value.toString());
1536+
key.setLength(0);
1537+
value.setLength(0);
1538+
expectingValue = false;
1539+
}
15331540
}
15341541
} else if (c == '=' && !inQuotes) {
15351542
expectingValue = true;

src/test/java/com/github/underscore/LodashTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import static java.util.Collections.singletonList;
3434

3535
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.params.ParameterizedTest;
37+
import org.junit.jupiter.params.provider.CsvSource;
3638

3739
import java.io.IOException;
3840
import java.net.URISyntaxException;
@@ -1152,6 +1154,87 @@ void xmpToJson6() {
11521154
+ "</root>"));
11531155
}
11541156

1157+
@Test
1158+
void xmpToJson7() {
1159+
assertEquals(
1160+
"{\n"
1161+
+ " \"Comment\": {\n"
1162+
+ " \"-stringValue\": \"a\",\n"
1163+
+ " \"-self-closing\": \"true\"\n"
1164+
+ " },\n"
1165+
+ " \"#omit-xml-declaration\": \"yes\"\n"
1166+
+ "}",
1167+
U.xmlToJson("<Comment stringValue='a'/>"));
1168+
assertEquals(
1169+
"{\n"
1170+
+ " \"Comment\": {\n"
1171+
+ " },\n"
1172+
+ " \"#omit-xml-declaration\": \"yes\"\n"
1173+
+ "}",
1174+
U.xmlToJson("<Comment stringValue='a\"'/>"));
1175+
assertEquals(
1176+
"{\n"
1177+
+ " \"Comment\": {\n"
1178+
+ " \"-stringValue\": \"a'\",\n"
1179+
+ " \"-self-closing\": \"true\"\n"
1180+
+ " },\n"
1181+
+ " \"#omit-xml-declaration\": \"yes\"\n"
1182+
+ "}",
1183+
U.xmlToJson("<Comment stringValue=\"a'\"/>"));
1184+
assertThrows(
1185+
IllegalArgumentException.class, () -> U.xmlToJson("<Comment stringValue=\"a'/>"));
1186+
assertThrows(
1187+
IllegalArgumentException.class, () -> U.xmlToJson("<Comment stringValue='a\"/>"));
1188+
}
1189+
1190+
@ParameterizedTest(name = "{0}")
1191+
@CsvSource(delimiter = '|', value = {
1192+
// input | expected (k=v,k=v)
1193+
"key=\"value\" | key=value",
1194+
"key='value' | key=value",
1195+
"a=\"1\" b='2' | a=1,b=2",
1196+
"key=\"it's a value\" | key=it's a value",
1197+
"key='say \"hi\"' | key=say \"hi\"",
1198+
"key=\"a=b=c\" | key=a=b=c",
1199+
"key=\"\" | key=",
1200+
" key =\"value\" | key=value",
1201+
"data-id=\"5\" | data-id=5",
1202+
"x==\"y\" | x=y",
1203+
"k=\"first\" k=\"second\" | k=second",
1204+
})
1205+
void parses(String input, String expected) {
1206+
assertEquals(parse(expected), Xml.parseAttributes(input));
1207+
}
1208+
1209+
@ParameterizedTest(name = "empty: \"{0}\"")
1210+
@CsvSource({
1211+
"''",
1212+
"\"orphan\"",
1213+
"lonekey",
1214+
"key=\"value",
1215+
})
1216+
void producesNothing(String input) {
1217+
assertTrue(Xml.parseAttributes(input).isEmpty());
1218+
}
1219+
1220+
@Test
1221+
void preservesInsertionOrder() {
1222+
assertEquals("[z, a, m]",
1223+
Xml.parseAttributes("z=\"1\" a=\"2\" m=\"3\"").keySet().toString());
1224+
}
1225+
1226+
// builds expected map from "k=v,k=v"
1227+
private static Map<String, String> parse(String s) {
1228+
Map<String, String> m = new LinkedHashMap<>();
1229+
if (s != null && !s.isEmpty()) {
1230+
for (String pair : s.split(",", -1)) {
1231+
int i = pair.indexOf('=');
1232+
m.put(pair.substring(0, i), pair.substring(i + 1));
1233+
}
1234+
}
1235+
return m;
1236+
}
1237+
11551238
@Test
11561239
void xmlToJsonMinimum() {
11571240
assertEquals(

0 commit comments

Comments
 (0)