From 250f74ef4df7ef32dc3d2d4d5e4a2c4fc25ca333 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 19 Jul 2020 18:51:14 +0530 Subject: [PATCH 1/7] Added type conversion support --- src/main/java/org/json/XML.java | 36 +++++++++++++++++-- .../java/org/json/XMLParserConfiguration.java | 28 +++++++++++++-- src/test/java/org/json/junit/XMLTest.java | 31 ++++++++++++++-- 3 files changed, 88 insertions(+), 7 deletions(-) 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); + } + +} From 61c1a882d6851058520dbfb71fae47276f5c9080 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Mon, 3 Aug 2020 08:54:59 +0530 Subject: [PATCH 2/7] Added configuration support for type conversion using Map --- src/main/java/org/json/XML.java | 25 ++++++------- .../java/org/json/XMLParserConfiguration.java | 20 ++++++----- .../java/org/json/XMLXsiTypeConverter.java | 5 +++ src/test/java/org/json/junit/XMLTest.java | 35 +++++++++++++------ 4 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/json/XMLXsiTypeConverter.java diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 0ad6ce477..004a1f89b 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -261,7 +261,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP String string; String tagName; Object token; - String typeCastClass; + XMLXsiTypeConverter xmlXsiTypeConverter; // Test for and skip past these forms: // @@ -341,7 +341,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP token = null; jsonObject = new JSONObject(); boolean nilAttributeFound = false; - typeCastClass = null; + xmlXsiTypeConverter = null; for (;;) { if (token == null) { token = x.nextToken(); @@ -360,9 +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 + } else if(config.xsiTypeMap != null && TYPE_ATTR.equals(string)) { - typeCastClass = (String) token; + xmlXsiTypeConverter = config.xsiTypeMap.get(token); } else if (!nilAttributeFound) { jsonObject.accumulate(string, config.isKeepStrings() @@ -401,9 +401,9 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP } else if (token instanceof String) { string = (String) token; if (string.length() > 0) { - if(typeCastClass != null) { + if(xmlXsiTypeConverter != null) { jsonObject.accumulate(config.getcDataTagName(), - stringToValue(string, typeCastClass)); + stringToValue(string, xmlXsiTypeConverter)); } else { jsonObject.accumulate(config.getcDataTagName(), config.isKeepStrings() ? string : stringToValue(string)); @@ -435,17 +435,12 @@ 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 + * @param typeConverter value converter to convert string to integer, boolean e.t.c * @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(); + public static Object stringToValue(String string, XMLXsiTypeConverter typeConverter) { + if(typeConverter != null) { + return typeConverter.convert(string); } return stringToValue(string); } diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index c57c8db36..b7057eef6 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -23,6 +23,9 @@ of this software and associated documentation files (the "Software"), to deal SOFTWARE. */ +import java.util.Map; + + /** * Configuration object for the XML parser. The configuration is immutable. * @author AylwardJ @@ -57,10 +60,9 @@ 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 + * This will allow type conversion for values in XML if xsi:type attribute is defined */ - public boolean useValueTypeCast; + public Map> xsiTypeMap; /** * Default parser configuration. Does not keep strings (tries to implicitly convert @@ -129,7 +131,7 @@ 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); + this(keepStrings, cDataTagName, convertNilAttributeToNull, null); } /** @@ -140,16 +142,16 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN * 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"}. + * @param xsiTypeMap new HashMap>() to parse values with attribute + * xsi:type="integer" as integer, xsi:type="string" as string + * null to use default behaviour. */ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, - final boolean convertNilAttributeToNull, final boolean useValueTypeCast ) { + final boolean convertNilAttributeToNull, final Map> xsiTypeMap ) { this.keepStrings = keepStrings; this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; - this.useValueTypeCast = useValueTypeCast; + this.xsiTypeMap = xsiTypeMap; } /** diff --git a/src/main/java/org/json/XMLXsiTypeConverter.java b/src/main/java/org/json/XMLXsiTypeConverter.java new file mode 100644 index 000000000..48e4ac18c --- /dev/null +++ b/src/main/java/org/json/XMLXsiTypeConverter.java @@ -0,0 +1,5 @@ +package org.json; + +public interface XMLXsiTypeConverter { + T convert(String value); +} diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 2b6f065d5..82c0b3969 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -38,6 +38,8 @@ of this software and associated documentation files (the "Software"), to deal import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; import org.json.JSONArray; import org.json.JSONException; @@ -45,6 +47,7 @@ of this software and associated documentation files (the "Software"), to deal import org.json.JSONTokener; import org.json.XML; import org.json.XMLParserConfiguration; +import org.json.XMLXsiTypeConverter; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -978,11 +981,10 @@ public void testIssue537CaseSensitiveHexUnEscapeDirect(){ */ @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()); - + String originalXml = "1234"; + String expectedJsonString = "{\"root\":{\"id\":{\"xsi:type\":\"string\",\"content\":1234}}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration()); Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); } @@ -991,12 +993,23 @@ public void testToJsonWithTypeWhenTypeConversionDisabled() { */ @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)); + String originalXml = "1234" + + "1234"; + String expectedJsonString = "{\"root\":{\"id2\":1234,\"id1\":\"1234\"}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Map> xsiTypeMap = new HashMap>(); + xsiTypeMap.put("string", new XMLXsiTypeConverter() { + @Override public String convert(final String value) { + return value; + } + }); + xsiTypeMap.put("integer", new XMLXsiTypeConverter() { + @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); } From 0a8091c95413edae87ec7196401224192938a946 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Wed, 5 Aug 2020 22:25:45 +0530 Subject: [PATCH 3/7] Added documentation --- .../java/org/json/XMLXsiTypeConverter.java | 61 +++++++++++++++++++ src/test/java/org/json/junit/XMLTest.java | 22 +++++++ 2 files changed, 83 insertions(+) diff --git a/src/main/java/org/json/XMLXsiTypeConverter.java b/src/main/java/org/json/XMLXsiTypeConverter.java index 48e4ac18c..e97fc944d 100644 --- a/src/main/java/org/json/XMLXsiTypeConverter.java +++ b/src/main/java/org/json/XMLXsiTypeConverter.java @@ -1,5 +1,66 @@ package org.json; +/* +Copyright (c) 2002 JSON.org +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * Type conversion configuration interface to be used with xsi:type attributes. + *
+ * 

XML Sample

+ * {@code + * + * 12345 + * 54321 + * + * } + *

JSON Output

+ * {@code + * { + * "root" : { + * "asString" : "12345", + * "asInt": 54321 + * } + * } + * } + * + *

Usage

+ * {@code + * Map> xsiTypeMap = new HashMap>(); + * xsiTypeMap.put("string", new XMLXsiTypeConverter() { + * @Override public String convert(final String value) { + * return value; + * } + * }); + * xsiTypeMap.put("integer", new XMLXsiTypeConverter() { + * @Override public Integer convert(final String value) { + * return Integer.valueOf(value); + * } + * }); + * } + *
+ * @author kumar529 + * @param + */ public interface XMLXsiTypeConverter { T convert(String value); } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 82c0b3969..a244856d3 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1013,4 +1013,26 @@ public void testToJsonWithTypeWhenTypeConversionEnabled() { Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); } + @Test + public void testToJsonWithXSITypeWhenTypeConversionEnabled() { + String originalXml = "1234554321"; + String expectedJsonString = "{\"root\":{\"asString\":\"12345\",\"asInt\":54321}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Map> xsiTypeMap = new HashMap>(); + xsiTypeMap.put("string", new XMLXsiTypeConverter() { + @Override public String convert(final String value) { + return value; + } + }); + xsiTypeMap.put("integer", new XMLXsiTypeConverter() { + @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); + } + } From 900a8cc94505bc0110bd18317ed0c75c43fccb4f Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 6 Aug 2020 07:29:32 +0530 Subject: [PATCH 4/7] Removed changes from depricated method --- src/main/java/org/json/XMLParserConfiguration.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index b7057eef6..e958cf394 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -114,7 +114,9 @@ public XMLParserConfiguration (final String cDataTagName) { */ @Deprecated public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) { - this(keepStrings, cDataTagName, false); + this.keepStrings = keepStrings; + this.cDataTagName = cDataTagName; + this.convertNilAttributeToNull = false; } /** @@ -131,7 +133,9 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN */ @Deprecated public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) { - this(keepStrings, cDataTagName, convertNilAttributeToNull, null); + this.keepStrings = keepStrings; + this.cDataTagName = cDataTagName; + this.convertNilAttributeToNull = convertNilAttributeToNull; } /** From 310f18fcdc288f04d083a844d90576dfff8db814 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Thu, 3 Sep 2020 11:17:10 +0530 Subject: [PATCH 5/7] Addressed comment --- src/main/java/org/json/XML.java | 4 +- .../java/org/json/XMLParserConfiguration.java | 40 ++++++++++++++++--- src/test/java/org/json/junit/XMLTest.java | 6 +-- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java index 004a1f89b..805a5c376 100644 --- a/src/main/java/org/json/XML.java +++ b/src/main/java/org/json/XML.java @@ -360,9 +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.xsiTypeMap != null + } else if(config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty() && TYPE_ATTR.equals(string)) { - xmlXsiTypeConverter = config.xsiTypeMap.get(token); + xmlXsiTypeConverter = config.getXsiTypeMap().get(token); } else if (!nilAttributeFound) { jsonObject.accumulate(string, config.isKeepStrings() diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index e958cf394..625d46365 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -23,6 +23,7 @@ of this software and associated documentation files (the "Software"), to deal SOFTWARE. */ +import java.util.Collections; import java.util.Map; @@ -62,7 +63,7 @@ public class XMLParserConfiguration { /** * This will allow type conversion for values in XML if xsi:type attribute is defined */ - public Map> xsiTypeMap; + private Map> xsiTypeMap; /** * Default parser configuration. Does not keep strings (tries to implicitly convert @@ -72,6 +73,7 @@ public XMLParserConfiguration () { this.keepStrings = false; this.cDataTagName = "content"; this.convertNilAttributeToNull = false; + this.xsiTypeMap = Collections.emptyMap(); } /** @@ -148,16 +150,15 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN * false to parse values with attribute xsi:nil="true" as {"xsi:nil":true}. * @param xsiTypeMap new HashMap>() to parse values with attribute * xsi:type="integer" as integer, xsi:type="string" as string - * null to use default behaviour. */ - public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, + private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull, final Map> xsiTypeMap ) { this.keepStrings = keepStrings; this.cDataTagName = cDataTagName; this.convertNilAttributeToNull = convertNilAttributeToNull; - this.xsiTypeMap = xsiTypeMap; + this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); } - + /** * Provides a new instance of the same configuration. */ @@ -171,7 +172,8 @@ protected XMLParserConfiguration clone() { return new XMLParserConfiguration( this.keepStrings, this.cDataTagName, - this.convertNilAttributeToNull + this.convertNilAttributeToNull, + this.xsiTypeMap ); } @@ -253,4 +255,30 @@ public XMLParserConfiguration withConvertNilAttributeToNull(final boolean newVal newConfig.convertNilAttributeToNull = newVal; return newConfig; } + + /** + * When parsing the XML into JSON, specifies that the values with attribute xsi:type + * will be converted to target type defined to client in this configuration + * Map> to parse values with attribute + * xsi:type="integer" as integer, xsi:type="string" as string + * @return {@link #xsiTypeMap} unmodifiable configuration map. + */ + public Map> getXsiTypeMap() { + return this.xsiTypeMap; + } + + /** + * When parsing the XML into JSON, specifies that the values with attribute xsi:type + * will be converted to target type defined to client in this configuration + * Map> to parse values with attribute + * xsi:type="integer" as integer, xsi:type="string" as string + * @param xsiTypeMap new HashMap>() to parse values with attribute + * xsi:type="integer" as integer, xsi:type="string" as string + * @return The existing configuration will not be modified. A new configuration is returned. + */ + public XMLParserConfiguration withXsiTypeMap(final Map> xsiTypeMap) { + XMLParserConfiguration newConfig = this.clone(); + newConfig.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); + return newConfig; + } } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index a244856d3..1cfd6b5a2 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1008,8 +1008,7 @@ public void testToJsonWithTypeWhenTypeConversionEnabled() { return Integer.valueOf(value); } }); - JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration(false, - "content", false, xsiTypeMap)); + JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap)); Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); } @@ -1030,8 +1029,7 @@ public void testToJsonWithXSITypeWhenTypeConversionEnabled() { return Integer.valueOf(value); } }); - JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration(false, - "content", false, xsiTypeMap)); + JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap)); Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); } From ed9658d5cb29fc281674e85d4e38cc4817caa643 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Fri, 4 Sep 2020 16:51:55 +0530 Subject: [PATCH 6/7] Corrected Javadoc --- src/main/java/org/json/XMLParserConfiguration.java | 6 +++--- src/main/java/org/json/XMLXsiTypeConverter.java | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index 625d46365..144c92a2e 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -259,7 +259,7 @@ public XMLParserConfiguration withConvertNilAttributeToNull(final boolean newVal /** * When parsing the XML into JSON, specifies that the values with attribute xsi:type * will be converted to target type defined to client in this configuration - * Map> to parse values with attribute + * {@code Map>} to parse values with attribute * xsi:type="integer" as integer, xsi:type="string" as string * @return {@link #xsiTypeMap} unmodifiable configuration map. */ @@ -270,9 +270,9 @@ public Map> getXsiTypeMap() { /** * When parsing the XML into JSON, specifies that the values with attribute xsi:type * will be converted to target type defined to client in this configuration - * Map> to parse values with attribute + * {@code Map>} to parse values with attribute * xsi:type="integer" as integer, xsi:type="string" as string - * @param xsiTypeMap new HashMap>() to parse values with attribute + * @param xsiTypeMap {@code new HashMap>()} to parse values with attribute * xsi:type="integer" as integer, xsi:type="string" as string * @return The existing configuration will not be modified. A new configuration is returned. */ diff --git a/src/main/java/org/json/XMLXsiTypeConverter.java b/src/main/java/org/json/XMLXsiTypeConverter.java index e97fc944d..0f8a8c332 100644 --- a/src/main/java/org/json/XMLXsiTypeConverter.java +++ b/src/main/java/org/json/XMLXsiTypeConverter.java @@ -26,14 +26,14 @@ of this software and associated documentation files (the "Software"), to deal /** * Type conversion configuration interface to be used with xsi:type attributes. *
- * 

XML Sample

+ * XML Sample * {@code * * 12345 * 54321 * * } - *

JSON Output

+ * JSON Output * {@code * { * "root" : { @@ -43,23 +43,23 @@ of this software and associated documentation files (the "Software"), to deal * } * } * - *

Usage

+ * Usage * {@code * Map> xsiTypeMap = new HashMap>(); * xsiTypeMap.put("string", new XMLXsiTypeConverter() { - * @Override public String convert(final String value) { + * @Override public String convert(final String value) { * return value; * } * }); * xsiTypeMap.put("integer", new XMLXsiTypeConverter() { - * @Override public Integer convert(final String value) { + * @Override public Integer convert(final String value) { * return Integer.valueOf(value); * } * }); * } *
* @author kumar529 - * @param + * @param return type of convert method */ public interface XMLXsiTypeConverter { T convert(String value); From 56d4130a863738b2cf61e1b8837aeb7a3b86f523 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 6 Sep 2020 11:17:10 +0530 Subject: [PATCH 7/7] Added shallow copy for config map --- .../java/org/json/XMLParserConfiguration.java | 4 ++- src/test/java/org/json/junit/XMLTest.java | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/json/XMLParserConfiguration.java b/src/main/java/org/json/XMLParserConfiguration.java index 144c92a2e..b9e752c28 100644 --- a/src/main/java/org/json/XMLParserConfiguration.java +++ b/src/main/java/org/json/XMLParserConfiguration.java @@ -24,6 +24,7 @@ of this software and associated documentation files (the "Software"), to deal */ import java.util.Collections; +import java.util.HashMap; import java.util.Map; @@ -278,7 +279,8 @@ public Map> getXsiTypeMap() { */ public XMLParserConfiguration withXsiTypeMap(final Map> xsiTypeMap) { XMLParserConfiguration newConfig = this.clone(); - newConfig.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); + Map> cloneXsiTypeMap = new HashMap>(xsiTypeMap); + newConfig.xsiTypeMap = Collections.unmodifiableMap(cloneXsiTypeMap); return newConfig; } } diff --git a/src/test/java/org/json/junit/XMLTest.java b/src/test/java/org/json/junit/XMLTest.java index 1cfd6b5a2..62ee516b2 100644 --- a/src/test/java/org/json/junit/XMLTest.java +++ b/src/test/java/org/json/junit/XMLTest.java @@ -1033,4 +1033,39 @@ public void testToJsonWithXSITypeWhenTypeConversionEnabled() { Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); } + @Test + public void testToJsonWithXSITypeWhenTypeConversionNotEnabledOnOne() { + String originalXml = "1234554321"; + String expectedJsonString = "{\"root\":{\"asString\":\"12345\",\"asInt\":54321}}"; + JSONObject expectedJson = new JSONObject(expectedJsonString); + Map> xsiTypeMap = new HashMap>(); + xsiTypeMap.put("string", new XMLXsiTypeConverter() { + @Override public String convert(final String value) { + return value; + } + }); + JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap)); + Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson); + } + + @Test + public void testXSITypeMapNotModifiable() { + Map> xsiTypeMap = new HashMap>(); + XMLParserConfiguration config = new XMLParserConfiguration().withXsiTypeMap(xsiTypeMap); + xsiTypeMap.put("string", new XMLXsiTypeConverter() { + @Override public String convert(final String value) { + return value; + } + }); + assertEquals("Config Conversion Map size is expected to be 0", 0, config.getXsiTypeMap().size()); + + try { + config.getXsiTypeMap().put("boolean", new XMLXsiTypeConverter() { + @Override public Boolean convert(final String value) { + return Boolean.valueOf(value); + } + }); + fail("Expected to be unable to modify the config"); + } catch (Exception ignored) { } + } }