Skip to content

Commit 250f74e

Browse files
author
Rahul Kumar
committed
Added type conversion support
1 parent 5541a6d commit 250f74e

File tree

3 files changed

+88
-7
lines changed

3 files changed

+88
-7
lines changed

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ of this software and associated documentation files (the "Software"), to deal
2626

2727
import java.io.Reader;
2828
import java.io.StringReader;
29+
import java.lang.reflect.Method;
2930
import java.math.BigDecimal;
3031
import java.math.BigInteger;
3132
import java.util.Iterator;
3233

34+
3335
/**
3436
* This provides static methods to convert an XML text into a JSONObject, and to
3537
* covert a JSONObject into an XML text.
@@ -72,6 +74,8 @@ public class XML {
7274
*/
7375
public static final String NULL_ATTR = "xsi:nil";
7476

77+
public static final String TYPE_ATTR = "xsi:type";
78+
7579
/**
7680
* Creates an iterator for navigating Code Points in a string instead of
7781
* 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
257261
String string;
258262
String tagName;
259263
Object token;
264+
String typeCastClass;
260265

261266
// Test for and skip past these forms:
262267
// <!-- ... -->
@@ -336,6 +341,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
336341
token = null;
337342
jsonObject = new JSONObject();
338343
boolean nilAttributeFound = false;
344+
typeCastClass = null;
339345
for (;;) {
340346
if (token == null) {
341347
token = x.nextToken();
@@ -354,6 +360,9 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
354360
&& NULL_ATTR.equals(string)
355361
&& Boolean.parseBoolean((String) token)) {
356362
nilAttributeFound = true;
363+
} else if(config.useValueTypeCast
364+
&& TYPE_ATTR.equals(string)) {
365+
typeCastClass = (String) token;
357366
} else if (!nilAttributeFound) {
358367
jsonObject.accumulate(string,
359368
config.isKeepStrings()
@@ -392,8 +401,13 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
392401
} else if (token instanceof String) {
393402
string = (String) token;
394403
if (string.length() > 0) {
395-
jsonObject.accumulate(config.getcDataTagName(),
396-
config.isKeepStrings() ? string : stringToValue(string));
404+
if(typeCastClass != null) {
405+
jsonObject.accumulate(config.getcDataTagName(),
406+
stringToValue(string, typeCastClass));
407+
} else {
408+
jsonObject.accumulate(config.getcDataTagName(),
409+
config.isKeepStrings() ? string : stringToValue(string));
410+
}
397411
}
398412

399413
} else if (token == LT) {
@@ -418,6 +432,24 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
418432
}
419433
}
420434

435+
/**
436+
* This method tries to convert the given string value to the target object
437+
* @param string String to convert
438+
* @param className target class name
439+
* @return JSON value of this string or the string
440+
*/
441+
public static Object stringToValue(String string, String className) {
442+
try {
443+
if(className.equals(String.class.getName())) return string;
444+
Class<?> clazz = Class.forName(className);
445+
Method method = clazz.getMethod("valueOf", String.class);
446+
return method.invoke(null, string);
447+
} catch (Exception e){
448+
e.printStackTrace();
449+
}
450+
return stringToValue(string);
451+
}
452+
421453
/**
422454
* This method is the same as {@link JSONObject#stringToValue(String)}.
423455
*

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ public class XMLParserConfiguration {
5656
*/
5757
private boolean convertNilAttributeToNull;
5858

59+
/**
60+
* When parsing the XML into JSON, specifies if values with attribute xsi:type="java.lang.Integer"
61+
* should be kept as attribute(false), or they should be converted to the given type
62+
*/
63+
public boolean useValueTypeCast;
64+
5965
/**
6066
* Default parser configuration. Does not keep strings (tries to implicitly convert
6167
* values), and the CDATA Tag Name is "content".
@@ -106,9 +112,7 @@ public XMLParserConfiguration (final String cDataTagName) {
106112
*/
107113
@Deprecated
108114
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) {
109-
this.keepStrings = keepStrings;
110-
this.cDataTagName = cDataTagName;
111-
this.convertNilAttributeToNull = false;
115+
this(keepStrings, cDataTagName, false);
112116
}
113117

114118
/**
@@ -125,9 +129,27 @@ public XMLParserConfiguration (final boolean keepStrings, final String cDataTagN
125129
*/
126130
@Deprecated
127131
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
132+
this(keepStrings, cDataTagName, convertNilAttributeToNull, false);
133+
}
134+
135+
/**
136+
* Configure the parser to use custom settings.
137+
* @param keepStrings <code>true</code> to parse all values as string.
138+
* <code>false</code> to try and convert XML string values into a JSON value.
139+
* @param cDataTagName <code>null</code> to disable CDATA processing. Any other value
140+
* to use that value as the JSONObject key name to process as CDATA.
141+
* @param convertNilAttributeToNull <code>true</code> to parse values with attribute xsi:nil="true" as null.
142+
* <code>false</code> to parse values with attribute xsi:nil="true" as {"xsi:nil":true}.
143+
* @param useValueTypeCast <code>true</code> to parse values with attribute xsi:type="java.lang.Integer" as
144+
* integer, xsi:type="java.lang.String" as string
145+
* <code>false</code> to parse values with attribute xsi:type="java.lang.Integer" as {"xsi:type":"java.lang.Integer"}.
146+
*/
147+
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
148+
final boolean convertNilAttributeToNull, final boolean useValueTypeCast ) {
128149
this.keepStrings = keepStrings;
129150
this.cDataTagName = cDataTagName;
130151
this.convertNilAttributeToNull = convertNilAttributeToNull;
152+
this.useValueTypeCast = useValueTypeCast;
131153
}
132154

133155
/**

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -972,5 +972,32 @@ public void testIssue537CaseSensitiveHexUnEscapeDirect(){
972972

973973
assertEquals("Case insensitive Entity unescape", expectedStr, actualStr);
974974
}
975-
976-
}
975+
976+
/**
977+
* test passes when xsi:type="java.lang.String" not converting to string
978+
*/
979+
@Test
980+
public void testToJsonWithTypeWhenTypeConversionDisabled() {
981+
final String originalXml = "<root><id xsi:type=\"java.lang.String\">1234</id></root>";
982+
final String expectedJsonString = "{\"root\":{\"id\":{\"xsi:type\":\"java.lang.String\",\"content\":1234}}}";
983+
final JSONObject expectedJson = new JSONObject(expectedJsonString);
984+
final JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration());
985+
986+
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
987+
}
988+
989+
/**
990+
* test passes when xsi:type="java.lang.String" converting to String
991+
*/
992+
@Test
993+
public void testToJsonWithTypeWhenTypeConversionEnabled() {
994+
final String originalXml = "<root><id1 xsi:type=\"java.lang.String\">1234</id1>"
995+
+ "<id2 xsi:type=\"java.lang.Integer\">1234</id2></root>";
996+
final String expectedJsonString = "{\"root\":{\"id2\":1234,\"id1\":\"1234\"}}";
997+
final JSONObject expectedJson = new JSONObject(expectedJsonString);
998+
final JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration(false,
999+
"content", false, true));
1000+
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
1001+
}
1002+
1003+
}

0 commit comments

Comments
 (0)