Skip to content

Commit 6c1bc06

Browse files
authored
Merge pull request stleary#820 from rudrajyotib/issue748
Close XML tag explicitly for empty tags with configuration.
2 parents b5b9f63 + 8ec822c commit 6c1bc06

File tree

4 files changed

+99
-13
lines changed

4 files changed

+99
-13
lines changed

src/main/java/org/json/XML.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -877,12 +877,25 @@ private static String toString(final Object object, final String tagName, final
877877
}
878878
}
879879
} else if ("".equals(value)) {
880-
sb.append(indent(indent));
881-
sb.append('<');
882-
sb.append(key);
883-
sb.append("/>");
884-
if(indentFactor > 0){
885-
sb.append("\n");
880+
if (config.isCloseEmptyTag()){
881+
sb.append(indent(indent));
882+
sb.append('<');
883+
sb.append(key);
884+
sb.append(">");
885+
sb.append("</");
886+
sb.append(key);
887+
sb.append(">");
888+
if (indentFactor > 0) {
889+
sb.append("\n");
890+
}
891+
}else {
892+
sb.append(indent(indent));
893+
sb.append('<');
894+
sb.append(key);
895+
sb.append("/>");
896+
if (indentFactor > 0) {
897+
sb.append("\n");
898+
}
886899
}
887900

888901
// Emit a new tag <k>

src/main/java/org/json/XMLParserConfiguration.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public class XMLParserConfiguration extends ParserConfiguration {
4343
*/
4444
private boolean convertNilAttributeToNull;
4545

46+
/**
47+
* When creating an XML from JSON Object, an empty tag by default will self-close.
48+
* If it has to be closed explicitly, with empty content between start and end tag,
49+
* this flag is to be turned on.
50+
*/
51+
private boolean closeEmptyTag;
52+
4653
/**
4754
* This will allow type conversion for values in XML if xsi:type attribute is defined
4855
*/
@@ -142,15 +149,17 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN
142149
* xsi:type="integer" as integer, xsi:type="string" as string
143150
* @param forceList <code>new HashSet<String>()</code> to parse the provided tags' values as arrays
144151
* @param maxNestingDepth <code>int</code> to limit the nesting depth
152+
* @param closeEmptyTag <code>boolean</code> to turn on explicit end tag for tag with empty value
145153
*/
146154
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
147155
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList,
148-
final int maxNestingDepth) {
156+
final int maxNestingDepth, final boolean closeEmptyTag) {
149157
super(keepStrings, maxNestingDepth);
150158
this.cDataTagName = cDataTagName;
151159
this.convertNilAttributeToNull = convertNilAttributeToNull;
152160
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
153161
this.forceList = Collections.unmodifiableSet(forceList);
162+
this.closeEmptyTag = closeEmptyTag;
154163
}
155164

156165
/**
@@ -169,7 +178,8 @@ protected XMLParserConfiguration clone() {
169178
this.convertNilAttributeToNull,
170179
this.xsiTypeMap,
171180
this.forceList,
172-
this.maxNestingDepth
181+
this.maxNestingDepth,
182+
this.closeEmptyTag
173183
);
174184
}
175185

@@ -303,4 +313,19 @@ public XMLParserConfiguration withForceList(final Set<String> forceList) {
303313
public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
304314
return super.withMaxNestingDepth(maxNestingDepth);
305315
}
316+
317+
/**
318+
* To enable explicit end tag with empty value.
319+
* @param closeEmptyTag
320+
* @return same instance of configuration with empty tag config updated
321+
*/
322+
public XMLParserConfiguration withCloseEmptyTag(boolean closeEmptyTag){
323+
XMLParserConfiguration clonedConfiguration = this.clone();
324+
clonedConfiguration.closeEmptyTag = closeEmptyTag;
325+
return clonedConfiguration;
326+
}
327+
328+
public boolean isCloseEmptyTag() {
329+
return this.closeEmptyTag;
330+
}
306331
}

src/test/java/org/json/junit/XMLConfigurationTest.java

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
Public Domain.
55
*/
66

7-
import static org.junit.Assert.assertEquals;
8-
import static org.junit.Assert.assertNotEquals;
9-
import static org.junit.Assert.assertTrue;
10-
import static org.junit.Assert.fail;
11-
127
import java.io.File;
138
import java.io.FileReader;
149
import java.io.FileWriter;
@@ -27,6 +22,8 @@
2722
import org.junit.Test;
2823
import org.junit.rules.TemporaryFolder;
2924

25+
import static org.junit.Assert.*;
26+
3027

3128
/**
3229
* Tests for JSON-Java XML.java with XMLParserConfiguration.java
@@ -557,6 +554,37 @@ public void shouldHandleNullNodeValue()
557554
assertEquals(actualXML, resultXML);
558555
}
559556

557+
@Test
558+
public void shouldHandleEmptyNodeValue()
559+
{
560+
JSONObject inputJSON = new JSONObject();
561+
inputJSON.put("Emptyness", "");
562+
String expectedXmlWithoutExplicitEndTag = "<Emptyness/>";
563+
String expectedXmlWithExplicitEndTag = "<Emptyness></Emptyness>";
564+
assertEquals(expectedXmlWithoutExplicitEndTag, XML.toString(inputJSON, null,
565+
new XMLParserConfiguration().withCloseEmptyTag(false)));
566+
assertEquals(expectedXmlWithExplicitEndTag, XML.toString(inputJSON, null,
567+
new XMLParserConfiguration().withCloseEmptyTag(true)));
568+
}
569+
570+
@Test
571+
public void shouldKeepConfigurationIntactAndUpdateCloseEmptyTagChoice()
572+
{
573+
XMLParserConfiguration keepStrings = XMLParserConfiguration.KEEP_STRINGS;
574+
XMLParserConfiguration keepStringsAndCloseEmptyTag = keepStrings.withCloseEmptyTag(true);
575+
XMLParserConfiguration keepDigits = keepStringsAndCloseEmptyTag.withKeepStrings(false);
576+
XMLParserConfiguration keepDigitsAndNoCloseEmptyTag = keepDigits.withCloseEmptyTag(false);
577+
assertTrue(keepStrings.isKeepStrings());
578+
assertFalse(keepStrings.isCloseEmptyTag());
579+
assertTrue(keepStringsAndCloseEmptyTag.isKeepStrings());
580+
assertTrue(keepStringsAndCloseEmptyTag.isCloseEmptyTag());
581+
assertFalse(keepDigits.isKeepStrings());
582+
assertTrue(keepDigits.isCloseEmptyTag());
583+
assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepStrings());
584+
assertFalse(keepDigitsAndNoCloseEmptyTag.isCloseEmptyTag());
585+
586+
}
587+
560588
/**
561589
* Investigate exactly how the "content" keyword works
562590
*/

src/test/java/org/json/junit/XMLTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,26 @@ public void testIndentComplicatedJsonObject(){
11771177

11781178

11791179
}
1180+
1181+
@Test
1182+
public void shouldCreateExplicitEndTagWithEmptyValueWhenConfigured(){
1183+
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
1184+
JSONObject jsonObject = new JSONObject(jsonString);
1185+
String expectedXmlString = "<encloser><outer><innerOne></innerOne><innerTwo>two</innerTwo></outer></encloser>";
1186+
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(true));
1187+
assertEquals(expectedXmlString, xmlForm);
1188+
}
1189+
1190+
@Test
1191+
public void shouldNotCreateExplicitEndTagWithEmptyValueWhenNotConfigured(){
1192+
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
1193+
JSONObject jsonObject = new JSONObject(jsonString);
1194+
String expectedXmlString = "<encloser><outer><innerOne/><innerTwo>two</innerTwo></outer></encloser>";
1195+
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(false));
1196+
assertEquals(expectedXmlString, xmlForm);
1197+
}
1198+
1199+
11801200
@Test
11811201
public void testIndentSimpleJsonObject(){
11821202
String str = "{ \"employee\": { \n" +

0 commit comments

Comments
 (0)