Research on the order of output key value pairs of JsonObject toString()

Keywords: JSON Java Android Mobile

There is a set of interface in the background, which needs to sort the parameters, so a set of sorting method is written. Before the interface is requested, all parameters are sorted. Here, JsonObject and its own toString method are used. During the test, it is found that the running of the program on a Android 4.4 system's mobile phone is not the expected result, so the following research is carried out

After analysis, it is found that the reason for the problem is that the output key value pair order of JsonObject toString is not fixed, and it is not necessarily output according to the order in which we put elements, so there is a problem in the order in which parameters are passed later. Let's see why the output order of object.toString is not fixed

The source code of JsonObject is as follows:

public JSONObject() {  
    nameValuePairs = new HashMap<String, Object>();  
} 
public JSONObject put(String name, Object value) throws JSONException {  
        if (value == null) {  
            nameValuePairs.remove(name);  
            return this;  
        }  
        if (value instanceof Number) {  
            // deviate from the original by checking all Numbers, not just floats & doubles  
            JSON.checkDouble(((Number) value).doubleValue());  
        }  
        nameValuePairs.put(checkName(name), value);  
        return this;  
    } 

We found that HashMap is used to store data in JsonObject, while HashMap does not store data in sequence.
To make the output order of object.toString, you need to customize a JsonObject, and use ordered linked HashMap instead of unordered HashMap for data storage

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedJsonObject extends JSONObject {

    private LinkedHashMap<Object, Object> nameValuePairs;

    public LinkedJsonObject() {
        nameValuePairs = new LinkedHashMap<>();
    }

    @Override
    public JSONObject put(String name, int value) throws JSONException {
        return put(name, value);
    }

    @Override
    public JSONObject put(String name, long value) throws JSONException {
        return put(name, value);
    }

    @Override
    public JSONObject put(String name, double value) throws JSONException {
        return put(name, value);
    }

    @Override
    public JSONObject put(String name, boolean value) throws JSONException {
        return put(name, value);
    }

    @Override
    public JSONObject put(String name, Object value) throws JSONException {

        checkName(name);

        if (value == null) {
            nameValuePairs.remove(name);
            return this;
        }
        if (value instanceof Double) {
            if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
                throw new JSONException("JSON does not allow non-finite numbers.");
            }
        } else if (value instanceof Float) {
            if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
                throw new JSONException("JSON does not allow non-finite numbers.");
            }
        }
        nameValuePairs.put(name, value);

        return this;
    }


    String checkName(String name) throws JSONException {
        if (name == null) {
            throw new JSONException("Names must be non-null");
        }
        return name;
    }

    public String toString() {
        try {
            Iterator<Object> keys = nameValuePairs.keySet().iterator();
            StringBuffer sb = new StringBuffer("{");

            while (keys.hasNext()) {
                if (sb.length() > 1) {
                    sb.append(',');
                }
                Object o = keys.next();
                sb.append(quote(o.toString()));
                sb.append(':');
                sb.append(valueToString(nameValuePairs.get(o)));
            }
            sb.append('}');
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }

    static String valueToString(Object value) throws JSONException {
        if (value == null || value.equals(null)) {
            return "null";
        }
        if (value instanceof JSONStringer) {
            Object o;
            try {
                o = ((JSONStringer) value).toString();
            } catch (Exception e) {
                throw new JSONException(e.getMessage());
            }
            if (o instanceof String) {
                return (String) o;
            }
            throw new JSONException("Bad value from toJSONString: " + o);
        }
        if (value instanceof Number) {
            return numberToString((Number) value);
        }
        if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) {
            return value.toString();
        }
        if (value instanceof Map) {
            return new JSONObject((Map) value).toString();
        }
        if (value instanceof Collection) {
            return new JSONArray((Collection) value).toString();
        }
        return quote(value.toString());
    }
}

In the latest Android source code, the datastore in JsonObject has been changed from HashMap to LinkedHashMap to achieve orderly output

Reference resources:
1.http://blog.csdn.net/ben0612/article/details/44591161

Posted by gmartin1215 on Tue, 31 Mar 2020 23:28:31 -0700