Skip to content

Commit

Permalink
Merge pull request #820 from rudrajyotib/issue748
Browse files Browse the repository at this point in the history
Close XML tag explicitly for empty tags with configuration.
  • Loading branch information
stleary authored Oct 31, 2023
2 parents b5b9f63 + 8ec822c commit 6c1bc06
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 13 deletions.
25 changes: 19 additions & 6 deletions src/main/java/org/json/XML.java
Original file line number Diff line number Diff line change
Expand Up @@ -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("</");
sb.append(key);
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 <k>
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/org/json/XMLParserConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -142,15 +149,17 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN
* xsi:type="integer" as integer, xsi:type="string" as string
* @param forceList <code>new HashSet<String>()</code> to parse the provided tags' values as arrays
* @param maxNestingDepth <code>int</code> to limit the nesting depth
* @param closeEmptyTag <code>boolean</code> to turn on explicit end tag for tag with empty value
*/
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> 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;
}

/**
Expand All @@ -169,7 +178,8 @@ protected XMLParserConfiguration clone() {
this.convertNilAttributeToNull,
this.xsiTypeMap,
this.forceList,
this.maxNestingDepth
this.maxNestingDepth,
this.closeEmptyTag
);
}

Expand Down Expand Up @@ -303,4 +313,19 @@ public XMLParserConfiguration withForceList(final Set<String> 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;
}
}
38 changes: 33 additions & 5 deletions src/test/java/org/json/junit/XMLConfigurationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -557,6 +554,37 @@ public void shouldHandleNullNodeValue()
assertEquals(actualXML, resultXML);
}

@Test
public void shouldHandleEmptyNodeValue()
{
JSONObject inputJSON = new JSONObject();
inputJSON.put("Emptyness", "");
String expectedXmlWithoutExplicitEndTag = "<Emptyness/>";
String expectedXmlWithExplicitEndTag = "<Emptyness></Emptyness>";
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
*/
Expand Down
20 changes: 20 additions & 0 deletions src/test/java/org/json/junit/XMLTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,26 @@ public void testIndentComplicatedJsonObject(){


}

@Test
public void shouldCreateExplicitEndTagWithEmptyValueWhenConfigured(){
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
JSONObject jsonObject = new JSONObject(jsonString);
String expectedXmlString = "<encloser><outer><innerOne></innerOne><innerTwo>two</innerTwo></outer></encloser>";
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 = "<encloser><outer><innerOne/><innerTwo>two</innerTwo></outer></encloser>";
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(false));
assertEquals(expectedXmlString, xmlForm);
}


@Test
public void testIndentSimpleJsonObject(){
String str = "{ \"employee\": { \n" +
Expand Down

0 comments on commit 6c1bc06

Please sign in to comment.