解决JSONObject的toString方法中,若数字小数点后为0,自动省略小数点后0的问题

前言:此问题网上多数是围绕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;
        }
    }
}