diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 533192313..efdfd9f66 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -877,12 +877,25 @@ private static String toString(final Object object, final String tagName, final } } } else if ("".equals(value)) { - sb.append(indent(indent)); - sb.append('<'); - sb.append(key); - sb.append("/>"); - if(indentFactor > 0){ - sb.append("\n"); + if (config.isCloseEmptyTag()){ + sb.append(indent(indent)); + sb.append('<'); + sb.append(key); + sb.append(">"); + sb.append(""); + if (indentFactor > 0) { + sb.append("\n"); + } + }else { + sb.append(indent(indent)); + sb.append('<'); + sb.append(key); + sb.append("/>"); + if (indentFactor > 0) { + sb.append("\n"); + } } // Emit a new tag diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index 566146d6d..2e4907f74 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -43,6 +43,13 @@ public class XMLParserConfiguration extends ParserConfiguration { */ private boolean convertNilAttributeToNull; + /** + * When creating an XML from JSON Object, an empty tag by default will self-close. + * If it has to be closed explicitly, with empty content between start and end tag, + * this flag is to be turned on. + */ + private boolean closeEmptyTag; + /** * This will allow type conversion for values in XML if xsi:type attribute is defined */ @@ -142,15 +149,17 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN * xsi:type="integer" as integer, xsi:type="string" as string * @param forceList new HashSet() to parse the provided tags' values as arrays * @param maxNestingDepth int to limit the nesting depth + * @param closeEmptyTag boolean to turn on explicit end tag for tag with empty value */ private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull, final Map> xsiTypeMap, final Set forceList, - final int maxNestingDepth) { + final int maxNestingDepth, final boolean closeEmptyTag) { super(keepStrings, maxNestingDepth); this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); this.forceList = Collections.unmodifiableSet(forceList); + this.closeEmptyTag = closeEmptyTag; } /** @@ -169,7 +178,8 @@ protected XMLParserConfiguration clone() { this.convertNilAttributeToNull, this.xsiTypeMap, this.forceList, - this.maxNestingDepth + this.maxNestingDepth, + this.closeEmptyTag ); } @@ -303,4 +313,19 @@ public XMLParserConfiguration withForceList(final Set forceList) { public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) { return super.withMaxNestingDepth(maxNestingDepth); } + + /** + * To enable explicit end tag with empty value. + * @param closeEmptyTag + * @return same instance of configuration with empty tag config updated + */ + public XMLParserConfiguration withCloseEmptyTag(boolean closeEmptyTag){ + XMLParserConfiguration clonedConfiguration = this.clone(); + clonedConfiguration.closeEmptyTag = closeEmptyTag; + return clonedConfiguration; + } + + public boolean isCloseEmptyTag() { + return this.closeEmptyTag; + } } diff --git a/src/test/java/org/json/junit/XMLConfigurationTest.java b/src/test/java/org/json/junit/XMLConfigurationTest.java index 2eaaf99e8..ffdc20cd2 100755 --- a/src/test/java/org/json/junit/XMLConfigurationTest.java +++ b/src/test/java/org/json/junit/XMLConfigurationTest.java @@ -4,11 +4,6 @@ Public Domain. */ -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.io.File; import java.io.FileReader; import java.io.FileWriter; @@ -27,6 +22,8 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; +import static org.junit.Assert.*; + /** * Tests for JSON-Java XML.java with XMLParserConfiguration.java @@ -557,6 +554,37 @@ public void shouldHandleNullNodeValue() assertEquals(actualXML, resultXML); } + @Test + public void shouldHandleEmptyNodeValue() + { + JSONObject inputJSON = new JSONObject(); + inputJSON.put("Emptyness", ""); + String expectedXmlWithoutExplicitEndTag = ""; + String expectedXmlWithExplicitEndTag = ""; + assertEquals(expectedXmlWithoutExplicitEndTag, XML.toString(inputJSON, null, + new XMLParserConfiguration().withCloseEmptyTag(false))); + assertEquals(expectedXmlWithExplicitEndTag, XML.toString(inputJSON, null, + new XMLParserConfiguration().withCloseEmptyTag(true))); + } + + @Test + public void shouldKeepConfigurationIntactAndUpdateCloseEmptyTagChoice() + { + XMLParserConfiguration keepStrings = XMLParserConfiguration.KEEP_STRINGS; + XMLParserConfiguration keepStringsAndCloseEmptyTag = keepStrings.withCloseEmptyTag(true); + XMLParserConfiguration keepDigits = keepStringsAndCloseEmptyTag.withKeepStrings(false); + XMLParserConfiguration keepDigitsAndNoCloseEmptyTag = keepDigits.withCloseEmptyTag(false); + assertTrue(keepStrings.isKeepStrings()); + assertFalse(keepStrings.isCloseEmptyTag()); + assertTrue(keepStringsAndCloseEmptyTag.isKeepStrings()); + assertTrue(keepStringsAndCloseEmptyTag.isCloseEmptyTag()); + assertFalse(keepDigits.isKeepStrings()); + assertTrue(keepDigits.isCloseEmptyTag()); + assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepStrings()); + assertFalse(keepDigitsAndNoCloseEmptyTag.isCloseEmptyTag()); + + } + /** * Investigate exactly how the "content" keyword works */ diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 63135eb00..d8aedb350 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1177,6 +1177,26 @@ public void testIndentComplicatedJsonObject(){ } + + @Test + public void shouldCreateExplicitEndTagWithEmptyValueWhenConfigured(){ + String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}"; + JSONObject jsonObject = new JSONObject(jsonString); + String expectedXmlString = "two"; + String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(true)); + assertEquals(expectedXmlString, xmlForm); + } + + @Test + public void shouldNotCreateExplicitEndTagWithEmptyValueWhenNotConfigured(){ + String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}"; + JSONObject jsonObject = new JSONObject(jsonString); + String expectedXmlString = "two"; + String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(false)); + assertEquals(expectedXmlString, xmlForm); + } + + @Test public void testIndentSimpleJsonObject(){ String str = "{ \"employee\": { \n" +