Added configuration support for type conversion using Map

This commit is contained in:
Rahul Kumar 2020-08-03 08:54:59 +05:30
parent 250f74ef4d
commit 61c1a882d6
4 changed files with 50 additions and 35 deletions

View File

@ -261,7 +261,7 @@ public class XML {
String string; String string;
String tagName; String tagName;
Object token; Object token;
String typeCastClass; XMLXsiTypeConverter<?> xmlXsiTypeConverter;
// Test for and skip past these forms: // Test for and skip past these forms:
// <!-- ... --> // <!-- ... -->
@ -341,7 +341,7 @@ public class XML {
token = null; token = null;
jsonObject = new JSONObject(); jsonObject = new JSONObject();
boolean nilAttributeFound = false; boolean nilAttributeFound = false;
typeCastClass = null; xmlXsiTypeConverter = null;
for (;;) { for (;;) {
if (token == null) { if (token == null) {
token = x.nextToken(); token = x.nextToken();
@ -360,9 +360,9 @@ public class XML {
&& NULL_ATTR.equals(string) && NULL_ATTR.equals(string)
&& Boolean.parseBoolean((String) token)) { && Boolean.parseBoolean((String) token)) {
nilAttributeFound = true; nilAttributeFound = true;
} else if(config.useValueTypeCast } else if(config.xsiTypeMap != null
&& TYPE_ATTR.equals(string)) { && TYPE_ATTR.equals(string)) {
typeCastClass = (String) token; xmlXsiTypeConverter = config.xsiTypeMap.get(token);
} else if (!nilAttributeFound) { } else if (!nilAttributeFound) {
jsonObject.accumulate(string, jsonObject.accumulate(string,
config.isKeepStrings() config.isKeepStrings()
@ -401,9 +401,9 @@ public class XML {
} else if (token instanceof String) { } else if (token instanceof String) {
string = (String) token; string = (String) token;
if (string.length() > 0) { if (string.length() > 0) {
if(typeCastClass != null) { if(xmlXsiTypeConverter != null) {
jsonObject.accumulate(config.getcDataTagName(), jsonObject.accumulate(config.getcDataTagName(),
stringToValue(string, typeCastClass)); stringToValue(string, xmlXsiTypeConverter));
} else { } else {
jsonObject.accumulate(config.getcDataTagName(), jsonObject.accumulate(config.getcDataTagName(),
config.isKeepStrings() ? string : stringToValue(string)); config.isKeepStrings() ? string : stringToValue(string));
@ -435,17 +435,12 @@ public class XML {
/** /**
* This method tries to convert the given string value to the target object * This method tries to convert the given string value to the target object
* @param string String to convert * @param string String to convert
* @param className target class name * @param typeConverter value converter to convert string to integer, boolean e.t.c
* @return JSON value of this string or the string * @return JSON value of this string or the string
*/ */
public static Object stringToValue(String string, String className) { public static Object stringToValue(String string, XMLXsiTypeConverter<?> typeConverter) {
try { if(typeConverter != null) {
if(className.equals(String.class.getName())) return string; return typeConverter.convert(string);
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod("valueOf", String.class);
return method.invoke(null, string);
} catch (Exception e){
e.printStackTrace();
} }
return stringToValue(string); return stringToValue(string);
} }

View File

@ -23,6 +23,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
import java.util.Map;
/** /**
* Configuration object for the XML parser. The configuration is immutable. * Configuration object for the XML parser. The configuration is immutable.
* @author AylwardJ * @author AylwardJ
@ -57,10 +60,9 @@ public class XMLParserConfiguration {
private boolean convertNilAttributeToNull; private boolean convertNilAttributeToNull;
/** /**
* When parsing the XML into JSON, specifies if values with attribute xsi:type="java.lang.Integer" * This will allow type conversion for values in XML if xsi:type attribute is defined
* should be kept as attribute(false), or they should be converted to the given type
*/ */
public boolean useValueTypeCast; public Map<String, XMLXsiTypeConverter<?>> xsiTypeMap;
/** /**
* Default parser configuration. Does not keep strings (tries to implicitly convert * Default parser configuration. Does not keep strings (tries to implicitly convert
@ -129,7 +131,7 @@ public class XMLParserConfiguration {
*/ */
@Deprecated @Deprecated
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) { public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
this(keepStrings, cDataTagName, convertNilAttributeToNull, false); this(keepStrings, cDataTagName, convertNilAttributeToNull, null);
} }
/** /**
@ -140,16 +142,16 @@ public class XMLParserConfiguration {
* to use that value as the JSONObject key name to process as CDATA. * to use that value as the JSONObject key name to process as CDATA.
* @param convertNilAttributeToNull <code>true</code> to parse values with attribute xsi:nil="true" as null. * @param convertNilAttributeToNull <code>true</code> to parse values with attribute xsi:nil="true" as null.
* <code>false</code> to parse values with attribute xsi:nil="true" as {"xsi:nil":true}. * <code>false</code> to parse values with attribute xsi:nil="true" as {"xsi:nil":true}.
* @param useValueTypeCast <code>true</code> to parse values with attribute xsi:type="java.lang.Integer" as * @param xsiTypeMap <code>new HashMap<String, XMLXsiTypeConverter<?>>()</code> to parse values with attribute
* integer, xsi:type="java.lang.String" as string * xsi:type="integer" as integer, xsi:type="string" as string
* <code>false</code> to parse values with attribute xsi:type="java.lang.Integer" as {"xsi:type":"java.lang.Integer"}. * <code>null</code> to use default behaviour.
*/ */
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
final boolean convertNilAttributeToNull, final boolean useValueTypeCast ) { final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap ) {
this.keepStrings = keepStrings; this.keepStrings = keepStrings;
this.cDataTagName = cDataTagName; this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull; this.convertNilAttributeToNull = convertNilAttributeToNull;
this.useValueTypeCast = useValueTypeCast; this.xsiTypeMap = xsiTypeMap;
} }
/** /**

View File

@ -0,0 +1,5 @@
package org.json;
public interface XMLXsiTypeConverter<T> {
T convert(String value);
}

View File

@ -38,6 +38,8 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@ -45,6 +47,7 @@ import org.json.JSONObject;
import org.json.JSONTokener; import org.json.JSONTokener;
import org.json.XML; import org.json.XML;
import org.json.XMLParserConfiguration; import org.json.XMLParserConfiguration;
import org.json.XMLXsiTypeConverter;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@ -978,11 +981,10 @@ public class XMLTest {
*/ */
@Test @Test
public void testToJsonWithTypeWhenTypeConversionDisabled() { public void testToJsonWithTypeWhenTypeConversionDisabled() {
final String originalXml = "<root><id xsi:type=\"java.lang.String\">1234</id></root>"; String originalXml = "<root><id xsi:type=\"string\">1234</id></root>";
final String expectedJsonString = "{\"root\":{\"id\":{\"xsi:type\":\"java.lang.String\",\"content\":1234}}}"; String expectedJsonString = "{\"root\":{\"id\":{\"xsi:type\":\"string\",\"content\":1234}}}";
final JSONObject expectedJson = new JSONObject(expectedJsonString); JSONObject expectedJson = new JSONObject(expectedJsonString);
final JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration()); JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration());
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
} }
@ -991,12 +993,23 @@ public class XMLTest {
*/ */
@Test @Test
public void testToJsonWithTypeWhenTypeConversionEnabled() { public void testToJsonWithTypeWhenTypeConversionEnabled() {
final String originalXml = "<root><id1 xsi:type=\"java.lang.String\">1234</id1>" String originalXml = "<root><id1 xsi:type=\"string\">1234</id1>"
+ "<id2 xsi:type=\"java.lang.Integer\">1234</id2></root>"; + "<id2 xsi:type=\"integer\">1234</id2></root>";
final String expectedJsonString = "{\"root\":{\"id2\":1234,\"id1\":\"1234\"}}"; String expectedJsonString = "{\"root\":{\"id2\":1234,\"id1\":\"1234\"}}";
final JSONObject expectedJson = new JSONObject(expectedJsonString); JSONObject expectedJson = new JSONObject(expectedJsonString);
final JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration(false, Map<String, XMLXsiTypeConverter<?>> xsiTypeMap = new HashMap<String, XMLXsiTypeConverter<?>>();
"content", false, true)); xsiTypeMap.put("string", new XMLXsiTypeConverter<String>() {
@Override public String convert(final String value) {
return value;
}
});
xsiTypeMap.put("integer", new XMLXsiTypeConverter<Integer>() {
@Override public Integer convert(final String value) {
return Integer.valueOf(value);
}
});
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration(false,
"content", false, xsiTypeMap));
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
} }