前言:此问题网上多数是围绕FastJson做的应对,而JSONObject这块目前没有找到,特此记录下!
业务场景:
APP端上传json数据到服务端,服务端用的JSONObject方式解析,当json串中包含类似xx.00的数据时,用toString方法生成的String类型数据时,里面的xx.00被默认置为xx,又由于MD5加密此字符串生成的密钥与客户端传上来的不相等,导致插入数据失败。
调查原因:
后由调试服务端代码,发现是JSONObject里面就是这么做的:
JSONObject.class中toString()
public String toString() {
if (this.isNullObject()) {
return JSONNull.getInstance().toString();
} else {
try {
Iterator keys = this.keys();
StringBuffer sb = new StringBuffer("{");
while(keys.hasNext()) {
if (sb.length() > 1) {
sb.append(',');
}
Object o = keys.next();
sb.append(JSONUtils.quote(o.toString()));
sb.append(':');
sb.append(JSONUtils.valueToString(this.properties.get(o)));
}
sb.append('}');
return sb.toString();
} catch (Exception var4) {
return null;
}
}
}
JSONObject内数据结构是一个Map,可以看到用到主要工作是在JSONUtils.valueToString(this.properties.get(o)),这个方法里,进入
JSONUtils.class中ValueToString()
public static String valueToString(Object value) {
if (value != null && !isNull(value)) {
if (value instanceof JSONFunction) {
return ((JSONFunction)value).toString();
} else if (value instanceof JSONString) {
String o;
try {
o = ((JSONString)value).toJSONString();
} catch (Exception var3) {
throw new JSONException(var3);
}
if (o instanceof String) {
return (String)o;
} else {
throw new JSONException("Bad value from toJSONString: " + o);
}
} else if (value instanceof Number) {
return numberToString((Number)value);
} else {
return !(value instanceof Boolean) && !(value instanceof JSONObject) && !(value instanceof JSONArray) ? quote(value.toString()) : value.toString();
}
} else {
return "null";
}
}
这里看到,value类型为Nubmer就是我们要注意的,下面看看这个numberToString((Number)value)方法里的逻辑
public static String numberToString(Number n) {
if (n == null) {
throw new JSONException("Null pointer");
} else {
testValidity(n);
String s = n.toString();
if (s.indexOf(46) > 0 && s.indexOf(101) < 0 && s.indexOf(69) < 0) {
while(s.endsWith("0")) {
s = s.substring(0, s.length() - 1);
}
if (s.endsWith(".")) {
s = s.substring(0, s.length() - 1);
}
}
return s;
}
}
可以明确看到,造成这个问题的原因就在这里。
解决问题:
主要思路:通过反射拿到JSONObject中的map,对JSONUtils中value类型为Number时的方法进行修改,另一点是递归处理,针对多层嵌套的数据。直接上代码
public class JsonObjectWrapper {
private JSONObject mJsonObject;
private Map properties;
public JsonObjectWrapper(JSONObject object) {
this.mJsonObject = object;
this.properties = getJsonObjectPropertyMap();
}
public String toString() {
if (properties == null) {
return JSONNull.getInstance().toString();
}
try {
Iterator keys = this.keys();
StringBuffer sb = new StringBuffer("{");
while (keys.hasNext()) {
if (sb.length() > 1) {
sb.append(',');
}
Object o = keys.next();
sb.append(JSONUtils.quote(o.toString()));
sb.append(':');
sb.append(valueToString(this.properties.get(o)));
}
sb.append('}');
return sb.toString();
} catch (Exception var4) {
return null;
}
}
public Iterator keys() {
return this.keySet().iterator();
}
public Set keySet() {
return Collections.unmodifiableSet(this.properties.keySet());
}
private Map getJsonObjectPropertyMap() {
if (this.mJsonObject == null) {
return null;
}
Map property = null;
try {
Field mapField = JSONObject.class.getDeclaredField("properties");
mapField.setAccessible(true);
Object mapObject = mapField.get(this.mJsonObject);
if (mapObject != null && mapObject instanceof Map) {
property = (Map) mapObject;
}
} catch (Exception e) {
e.toString();
}
return property;
}
private String valueToString(Object value) {
if (value != null && !JSONUtils.isNull(value)) {
if (value instanceof JSONFunction) {
return value.toString();
} else if (value instanceof JSONString) {
String o;
try {
o = ((JSONString) value).toJSONString();
} catch (Exception var3) {
return null;
}
if (o instanceof String) {
return o;
} else {
return null;
}
} else if (value instanceof Number) {
return numberToString((Number) value);
} else if (value instanceof JSONObject) {
return new JsonObjectWrapper((JSONObject) value).toString();
} else if (value instanceof JSONArray) {
StringBuffer buffer = new StringBuffer("[");
for (int i = 0; i < ((JSONArray) value).size(); i++) {
if (buffer.length() > 1) {
buffer.append(',');
}
buffer.append(new JsonObjectWrapper((JSONObject) value).toString());
}
buffer.append(']');
return buffer.toString();
} else {
return !(value instanceof Boolean) ? JSONUtils.quote(value.toString()) : value.toString();
}
} else {
return "null";
}
}
private String numberToString(Number n) {
if (n == null) {
return null;
} else {
JSONUtils.testValidity(n);
String s = n.toString();
// if (s.indexOf(46) > 0 && s.indexOf(101) < 0 && s.indexOf(69) < 0) {
// while(s.endsWith("0")) {
// s = s.substring(0, s.length() - 1);
// }
//
// if (s.endsWith(".")) {
// s = s.substring(0, s.length() - 1);
// }
// }
return s;
}
}
}