diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index c81f15ff5..0ad6ce477 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -26,10 +26,12 @@ of this software and associated documentation files (the "Software"), to deal import java.io.Reader; import java.io.StringReader; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Iterator; + /** * This provides static methods to convert an XML text into a JSONObject, and to * covert a JSONObject into an XML text. @@ -72,6 +74,8 @@ public class XML { */ public static final String NULL_ATTR = "xsi:nil"; + public static final String TYPE_ATTR = "xsi:type"; + /** * Creates an iterator for navigating Code Points in a string instead of * characters. Once Java7 support is dropped, this can be replaced with @@ -257,6 +261,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP String string; String tagName; Object token; + String typeCastClass; // Test for and skip past these forms: // @@ -336,6 +341,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP token = null; jsonObject = new JSONObject(); boolean nilAttributeFound = false; + typeCastClass = null; for (;;) { if (token == null) { token = x.nextToken(); @@ -354,6 +360,9 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP && NULL_ATTR.equals(string) && Boolean.parseBoolean((String) token)) { nilAttributeFound = true; + } else if(config.useValueTypeCast + && TYPE_ATTR.equals(string)) { + typeCastClass = (String) token; } else if (!nilAttributeFound) { jsonObject.accumulate(string, config.isKeepStrings() @@ -392,8 +401,13 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP } else if (token instanceof String) { string = (String) token; if (string.length() > 0) { - jsonObject.accumulate(config.getcDataTagName(), - config.isKeepStrings() ? string : stringToValue(string)); + if(typeCastClass != null) { + jsonObject.accumulate(config.getcDataTagName(), + stringToValue(string, typeCastClass)); + } else { + jsonObject.accumulate(config.getcDataTagName(), + config.isKeepStrings() ? string : stringToValue(string)); + } } } else if (token == LT) { @@ -418,6 +432,24 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP } } + /** + * This method tries to convert the given string value to the target object + * @param string String to convert + * @param className target class name + * @return JSON value of this string or the string + */ + public static Object stringToValue(String string, String className) { + try { + if(className.equals(String.class.getName())) return 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); + } + /** * This method is the same as {@link JSONObject#stringToValue(String)}. * diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index cf5e10caa..c57c8db36 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -56,6 +56,12 @@ public class XMLParserConfiguration { */ private boolean convertNilAttributeToNull; + /** + * When parsing the XML into JSON, specifies if values with attribute xsi:type="java.lang.Integer" + * should be kept as attribute(false), or they should be converted to the given type + */ + public boolean useValueTypeCast; + /** * Default parser configuration. Does not keep strings (tries to implicitly convert * values), and the CDATA Tag Name is "content". @@ -106,9 +112,7 @@ public XMLParserConfiguration (final String cDataTagName) { */ @Deprecated public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) { - this.keepStrings = keepStrings; - this.cDataTagName = cDataTagName; - this.convertNilAttributeToNull = false; + this(keepStrings, cDataTagName, false); } /** @@ -125,9 +129,27 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN */ @Deprecated public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) { + this(keepStrings, cDataTagName, convertNilAttributeToNull, false); + } + + /** + * Configure the parser to use custom settings. + * @param keepStrings true to parse all values as string. + * false to try and convert XML string values into a JSON value. + * @param cDataTagName null to disable CDATA processing. Any other value + * to use that value as the JSONObject key name to process as CDATA. + * @param convertNilAttributeToNull true to parse values with attribute xsi:nil="true" as null. + * false to parse values with attribute xsi:nil="true" as {"xsi:nil":true}. + * @param useValueTypeCast true to parse values with attribute xsi:type="java.lang.Integer" as + * integer, xsi:type="java.lang.String" as string + * false to parse values with attribute xsi:type="java.lang.Integer" as {"xsi:type":"java.lang.Integer"}. + */ + public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, + final boolean convertNilAttributeToNull, final boolean useValueTypeCast ) { this.keepStrings = keepStrings; this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; + this.useValueTypeCast = useValueTypeCast; } /** diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index cf78350b4..2b6f065d5 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -972,5 +972,32 @@ public void testIssue537CaseSensitiveHexUnEscapeDirect(){ assertEquals("Case insensitive Entity unescape", expectedStr, actualStr); } - -} \ No newline at end of file + + /** + * test passes when xsi:type="java.lang.String" not converting to string + */ + @Test + public void testToJsonWithTypeWhenTypeConversionDisabled() { + final String originalXml = "1234"; + final String expectedJsonString = "{\"root\":{\"id\":{\"xsi:type\":\"java.lang.String\",\"content\":1234}}}"; + final JSONObject expectedJson = new JSONObject(expectedJsonString); + final JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration()); + + Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); + } + + /** + * test passes when xsi:type="java.lang.String" converting to String + */ + @Test + public void testToJsonWithTypeWhenTypeConversionEnabled() { + final String originalXml = "1234" + + "1234"; + final String expectedJsonString = "{\"root\":{\"id2\":1234,\"id1\":\"1234\"}}"; + final JSONObject expectedJson = new JSONObject(expectedJsonString); + final JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration(false, + "content", false, true)); + Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); + } + +}