diff --git a/src/main/java/org/json/JSONML.java b/src/main/java/org/json/JSONML.java index 6ec997061..937d7cfcf 100644 --- a/src/main/java/org/json/JSONML.java +++ b/src/main/java/org/json/JSONML.java @@ -14,6 +14,8 @@ */ public class JSONML { + private static final String CHILD_NODES = "childNodes"; + /** * Constructs a new JSONML object. * @deprecated (Utility class cannot be instantiated) @@ -205,7 +207,7 @@ private static Object parse( // attribute = value attribute = (String)token; - if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) { + if (!arrayForm && ("tagName".equals(attribute) || CHILD_NODES.equals(attribute))) { throw x.syntaxError("Reserved attribute."); } token = x.nextToken(); @@ -256,7 +258,7 @@ private static Object parse( } tagName = null; if (!arrayForm && newja.length() > 0) { - newjo.put("childNodes", newja); + newjo.put(CHILD_NODES, newja); } if (ja == null) { if (arrayForm) { @@ -648,7 +650,7 @@ public static String toString(JSONObject jo) throws JSONException { // Don't use the new entrySet API to maintain Android support for (final String key : jo.keySet()) { - if (!"tagName".equals(key) && !"childNodes".equals(key)) { + if (!"tagName".equals(key) && !CHILD_NODES.equals(key)) { XML.noSpace(key); value = jo.opt(key); if (value != null) { @@ -664,7 +666,7 @@ public static String toString(JSONObject jo) throws JSONException { //Emit content in body - ja = jo.optJSONArray("childNodes"); + ja = jo.optJSONArray(CHILD_NODES); if (ja == null) { sb.append('/'); sb.append('>'); diff --git a/src/test/java/org/json/junit/JSONMLTest.java b/src/test/java/org/json/junit/JSONMLTest.java index 93a6821d8..c83f73e24 100644 --- a/src/test/java/org/json/junit/JSONMLTest.java +++ b/src/test/java/org/json/junit/JSONMLTest.java @@ -1052,4 +1052,61 @@ public void testValidXMLToJSONObjectStillWorks() { } } + /** + * Tests that the childNodes attribute is reserved in object-form parsing. + */ + @Test + public void testToJSONObjectRejectsReservedChildNodesAttribute() { + String xmlStr = "

text

"; + for (boolean keepStrings : new boolean[] {false, true}) { + JSONException exception = assertThrows(JSONException.class, + () -> JSONML.toJSONObject(xmlStr, keepStrings)); + assertTrue(exception.getMessage().startsWith("Reserved attribute.")); + } + } + + /** + * Tests that the ordinary childNode attribute is preserved in object-form parsing. + */ + @Test + public void testToJSONObjectPreservesChildNodeAttribute() { + String xmlStr = "

text

"; + for (boolean keepStrings : new boolean[] {false, true}) { + JSONObject jsonObject = JSONML.toJSONObject(xmlStr, keepStrings); + assertEquals("p", jsonObject.getString("tagName")); + assertEquals("metadata", jsonObject.getString("childNode")); + assertEquals("text", jsonObject.getJSONArray("childNodes").getString(0)); + } + } + + /** + * Tests that the tagName attribute remains reserved in object-form parsing. + */ + @Test + public void testToJSONObjectRejectsReservedTagNameAttribute() { + String xmlStr = "

text

"; + for (boolean keepStrings : new boolean[] {false, true}) { + JSONException exception = assertThrows(JSONException.class, + () -> JSONML.toJSONObject(xmlStr, keepStrings)); + assertTrue(exception.getMessage().startsWith("Reserved attribute.")); + } + } + + /** + * Tests that childNode and childNodes attributes are preserved in array-form parsing. + */ + @Test + public void testToJSONArrayPreservesChildNodeAttributes() { + String xmlStr = "

text

"; + for (boolean keepStrings : new boolean[] {false, true}) { + JSONArray jsonArray = JSONML.toJSONArray(xmlStr, keepStrings); + assertEquals(3, jsonArray.length()); + assertEquals("p", jsonArray.getString(0)); + JSONObject attributes = jsonArray.getJSONObject(1); + assertEquals("singular", attributes.getString("childNode")); + assertEquals("plural", attributes.getString("childNodes")); + assertEquals("text", jsonArray.getString(2)); + } + } + }