From 995fb840f79bf8b9230a08e45cd2289fb21b3cc6 Mon Sep 17 00:00:00 2001 From: Pratik Tiwari Date: Fri, 2 Jan 2026 21:20:53 +0530 Subject: [PATCH 1/3] Fixes the issue of losing the array if an empty forceList element or a tag is in the middle or the end --- src/main/java/org/json/XML.java | 7 +- .../org/json/junit/XMLConfigurationTest.java | 108 ++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index e14bb34e9..d0216f5d8 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -391,7 +391,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP context.append(tagName, JSONObject.NULL); } else if (jsonObject.length() > 0) { context.append(tagName, jsonObject); - } else { + } else if(context.isEmpty() && (context.opt(tagName) == null || !(context.get(tagName) instanceof JSONArray))) { //avoids resetting the array in case of an empty tag in the middle or end context.put(tagName, new JSONArray()); } } else { @@ -451,7 +451,10 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP if (config.getForceList().contains(tagName)) { // Force the value to be an array if (jsonObject.length() == 0) { - context.put(tagName, new JSONArray()); + //avoids resetting the array in case of an empty element in the middle or end + if(context.length()==0 && context.opt(tagName) == null || !(context.get(tagName) instanceof JSONArray)) { + context.put(tagName, new JSONArray()); + } } else if (jsonObject.length() == 1 && jsonObject.opt(config.getcDataTagName()) != null) { context.append(tagName, jsonObject.opt(config.getcDataTagName())); diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index ca1980c8a..8edbe7984 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -1144,6 +1144,114 @@ public void testEmptyTagForceList() { Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject); } + @Test + public void testForceListWithLastElementAsEmptyTag(){ + final String originalXml = "1"; + final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withcDataTagName("content") + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListWithFirstElementAsEmptyTag(){ + final String originalXml = "1"; + final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withcDataTagName("content") + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListWithMiddleElementAsEmptyTag(){ + final String originalXml = "12"; + final String expectedJsonString = "{\"root\":{\"id\":[1,2]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withcDataTagName("content") + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListWithLastElementAsEmpty(){ + final String originalXml = "1"; + final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListWithFirstElementAsEmpty(){ + final String originalXml = "1"; + final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListWithMiddleElementAsEmpty(){ + final String originalXml = "12"; + final String expectedJsonString = "{\"root\":{\"id\":[1,2]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListEmptyAndEmptyTagsMixed(){ + final String originalXml = "12"; + final String expectedJsonString = "{\"root\":{\"id\":[1,2]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + final JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withKeepStrings(false) + .withForceList(forceListCandidates) + .withConvertNilAttributeToNull(true)); + assertEquals(expectedJsonString, json.toString()); + } + @Test public void testMaxNestingDepthIsSet() { XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL; From 7a8da886e7cc816ddd50f01ca3ae76673d07a671 Mon Sep 17 00:00:00 2001 From: Pratik Tiwari Date: Fri, 30 Jan 2026 19:29:46 +0530 Subject: [PATCH 2/3] Remove unnecessary conditions --- src/main/java/org/json/XML.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index d0216f5d8..539285d8b 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -391,7 +391,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP context.append(tagName, JSONObject.NULL); } else if (jsonObject.length() > 0) { context.append(tagName, jsonObject); - } else if(context.isEmpty() && (context.opt(tagName) == null || !(context.get(tagName) instanceof JSONArray))) { //avoids resetting the array in case of an empty tag in the middle or end + } else if(context.isEmpty()) { //avoids resetting the array in case of an empty tag in the middle or end context.put(tagName, new JSONArray()); } } else { @@ -452,7 +452,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP // Force the value to be an array if (jsonObject.length() == 0) { //avoids resetting the array in case of an empty element in the middle or end - if(context.length()==0 && context.opt(tagName) == null || !(context.get(tagName) instanceof JSONArray)) { + if(context.isEmpty()) { context.put(tagName, new JSONArray()); } } else if (jsonObject.length() == 1 From 510a03ac3609bd226c094f716fce74a44c64c663 Mon Sep 17 00:00:00 2001 From: Pratik Tiwari Date: Sat, 31 Jan 2026 10:32:09 +0530 Subject: [PATCH 3/3] Fixes #1040, Aligns non-forceList behaviour with forceList --- src/main/java/org/json/XML.java | 6 ++ .../org/json/junit/XMLConfigurationTest.java | 63 ++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 539285d8b..7e4b0bb0c 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -393,6 +393,11 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP context.append(tagName, jsonObject); } else if(context.isEmpty()) { //avoids resetting the array in case of an empty tag in the middle or end context.put(tagName, new JSONArray()); + if (jsonObject.isEmpty()){ + context.append(tagName, ""); + } + } else { + context.append(tagName, ""); } } else { if (nilAttributeFound) { @@ -455,6 +460,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP if(context.isEmpty()) { context.put(tagName, new JSONArray()); } + context.append(tagName, ""); } else if (jsonObject.length() == 1 && jsonObject.opt(config.getcDataTagName()) != null) { context.append(tagName, jsonObject.opt(config.getcDataTagName())); diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 8edbe7984..e8ff3b60c 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -1092,7 +1092,7 @@ public void testEmptyForceList() { ""; String expectedStr = - "{\"addresses\":[]}"; + "{\"addresses\":[\"\"]}"; Set forceList = new HashSet(); forceList.add("addresses"); @@ -1130,7 +1130,7 @@ public void testEmptyTagForceList() { ""; String expectedStr = - "{\"addresses\":[]}"; + "{\"addresses\":[\"\"]}"; Set forceList = new HashSet(); forceList.add("addresses"); @@ -1147,7 +1147,7 @@ public void testEmptyTagForceList() { @Test public void testForceListWithLastElementAsEmptyTag(){ final String originalXml = "1"; - final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + final String expectedJsonString = "{\"root\":{\"id\":[1,\"\"]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); @@ -1163,7 +1163,7 @@ public void testForceListWithLastElementAsEmptyTag(){ @Test public void testForceListWithFirstElementAsEmptyTag(){ final String originalXml = "1"; - final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + final String expectedJsonString = "{\"root\":{\"id\":[\"\",1]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); @@ -1179,7 +1179,7 @@ public void testForceListWithFirstElementAsEmptyTag(){ @Test public void testForceListWithMiddleElementAsEmptyTag(){ final String originalXml = "12"; - final String expectedJsonString = "{\"root\":{\"id\":[1,2]}}"; + final String expectedJsonString = "{\"root\":{\"id\":[1,\"\",2]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); @@ -1195,8 +1195,7 @@ public void testForceListWithMiddleElementAsEmptyTag(){ @Test public void testForceListWithLastElementAsEmpty(){ final String originalXml = "1"; - final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; - + final String expectedJsonString = "{\"root\":{\"id\":[1,\"\"]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); final JSONObject json = XML.toJSONObject(originalXml, @@ -1210,7 +1209,7 @@ public void testForceListWithLastElementAsEmpty(){ @Test public void testForceListWithFirstElementAsEmpty(){ final String originalXml = "1"; - final String expectedJsonString = "{\"root\":{\"id\":[1]}}"; + final String expectedJsonString = "{\"root\":{\"id\":[\"\",1]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); @@ -1225,7 +1224,7 @@ public void testForceListWithFirstElementAsEmpty(){ @Test public void testForceListWithMiddleElementAsEmpty(){ final String originalXml = "12"; - final String expectedJsonString = "{\"root\":{\"id\":[1,2]}}"; + final String expectedJsonString = "{\"root\":{\"id\":[1,\"\",2]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); @@ -1240,7 +1239,7 @@ public void testForceListWithMiddleElementAsEmpty(){ @Test public void testForceListEmptyAndEmptyTagsMixed(){ final String originalXml = "12"; - final String expectedJsonString = "{\"root\":{\"id\":[1,2]}}"; + final String expectedJsonString = "{\"root\":{\"id\":[\"\",\"\",1,\"\",\"\",2]}}"; HashSet forceListCandidates = new HashSet<>(); forceListCandidates.add("id"); @@ -1252,6 +1251,50 @@ public void testForceListEmptyAndEmptyTagsMixed(){ assertEquals(expectedJsonString, json.toString()); } + @Test + public void testForceListConsistencyWithDefault() { + final String originalXml = "01"; + final String expectedJsonString = "{\"root\":{\"id\":[0,1,\"\",\"\"]}}"; + + // confirm expected result of default array-of-tags processing + JSONObject json = XML.toJSONObject(originalXml); + assertEquals(expectedJsonString, json.toString()); + + // confirm forceList array-of-tags processing is consistent with default processing + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withForceList(forceListCandidates)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListInitializesAnArrayWithAnEmptyElement(){ + final String originalXml = ""; + final String expectedJsonString = "{\"root\":{\"id\":[\"\"]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withForceList(forceListCandidates)); + assertEquals(expectedJsonString, json.toString()); + } + + @Test + public void testForceListInitializesAnArrayWithAnEmptyTag(){ + final String originalXml = ""; + final String expectedJsonString = "{\"root\":{\"id\":[\"\"]}}"; + + HashSet forceListCandidates = new HashSet<>(); + forceListCandidates.add("id"); + JSONObject json = XML.toJSONObject(originalXml, + new XMLParserConfiguration() + .withForceList(forceListCandidates)); + assertEquals(expectedJsonString, json.toString()); + } + @Test public void testMaxNestingDepthIsSet() { XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;