Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close XML tag explicitly for empty tags with configuration. #820

Merged
merged 4 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
28 changes: 26 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,18 @@ 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){
this.closeEmptyTag = closeEmptyTag;
rudrajyotib marked this conversation as resolved.
Show resolved Hide resolved
return this;
}

public boolean isCloseEmptyTag() {
return this.closeEmptyTag;
}
}
11 changes: 11 additions & 0 deletions src/test/java/org/json/junit/XMLConfigurationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,17 @@ 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)));
}

/**
* 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