【Android 一般进阶】固定JSONObject.toString()键值对顺序

org.json.JSONObject相信很多人都用过,例如:服务端返回给客户端的数据格式是JSONObject的,那我们通过对它进行解析,把它显示在界面上。

有时候希望服务器跟客户端通信的JSONObjectt的键值对顺序是固定的。

例如:

客户端提交一个JSONObject格式的数据,里面有三个键值对,分别是"a":"a" "b":"b" "c":"c"

服务端原封不动返回给客户端,这个客户端希望接到的是这样的

{"a":"a","b":"b","c":"c"}但JSONObject的键值对顺序几乎不能确定的,无规律可循,,验证如下:1. <pre name="code" class="html">JSONObject object=new JSONObject();try {object.putOpt("b", "b");object.putOpt("a", "a");object.putOpt("c", "c");} catch (JSONException e) {// TODO Auto-generated catch blocke.printStackTrace();}

object.toString(),得到的是

{"b":"b","c":"c","a":"a"}与put进去时候的顺序不一致。

2.

JSONObject object=new JSONObject();try {object.putOpt("1", "1");object.putOpt("2", "2");object.putOpt("3", "3");} catch (JSONException e) {// TODO Auto-generated catch blocke.printStackTrace();}object.toString(),得到的是{"3":"3","2":"2","1":"1"}与put进去时候的顺序不一致3.JSONObject object=new JSONObject();try {object.putOpt("我", "我");object.putOpt("你", "你");object.putOpt("他", "他");} catch (JSONException e) {// TODO Auto-generated catch blocke.printStackTrace();}object.toString(),得到的是{"你":"你","他":"他","我":"我"}

与put进去时候的顺序不一致。

以上三个例子,无论键和值是数字、字母还是中文,JSONObject put进去的顺序喝toString得到的顺序,是不一致的。

如果还有疑问,我们看看JSONObject的源码吧。

先看构造方法:

public JSONObject() {nameValuePairs = new HashMap<String, Object>();}再看putOpt 以及put方法: public JSONObject putOpt(String name, Object value) throws JSONException {if (name == null || value == null) {return this;}return put(name, value);}    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;    }这下可明白了吧,其实JSONObject本质是用HashMap实现的,而HashMap是散列的,是链式存储结构。

HashMap的存储过程,根据该元素自身提供的hashcode计算出散列值(在这一点上,就可以知道,元素放进去的位置是无法确定的,只有在获取hashcode后才知道),该值就是数组的下标,然后将该元素放入数组位置的链表中。

那么如何固定它的顺序呢,put进去是我们想要的呢?有两个方法:自定义JSONObject(不用HashMap改用LinkHashMap实现)或使用com.alibaba.fastjson.annotation.JSONType标注

1.自定义JSONObject(不用HashMap改用LinkHashMap实现),LinkedHashMap是有序的,代替无序的HashMap,把父类用到HashMap的地方都改成LinkedHashMap即可,主要是put跟toString的几个方法。

public class MyJSONObject extends JSONObject {private LinkedHashMap<Object, Object> mHashMap;public ChatMsgJSONObject() {mHashMap = new LinkedHashMap<Object, Object>();}@Overridepublic JSONObject put(String name, boolean value) throws JSONException {// TODO Auto-generated method stubreturn put(name, value);}@Overridepublic JSONObject put(String name, double value) throws JSONException {// TODO Auto-generated method stubreturn put(name, value);}@Overridepublic JSONObject put(String name, int value) throws JSONException {// TODO Auto-generated method stubreturn put(name, value);}@Overridepublic JSONObject put(String name, long value) throws JSONException {// TODO Auto-generated method stubreturn put(name, value);}public JSONObject put(String key, Object value) throws JSONException {if (key == null) {throw new JSONException("Null key.");}if (value != null) {testValidity(value);mHashMap.put(key, value);} else {remove(key);}return this;}public Object remove(String key) {return mHashMap.remove(key);}static void testValidity(Object o) throws JSONException {if (o != null) {if (o instanceof Double) {if (((Double) o).isInfinite() || ((Double) o).isNaN()) {throw new JSONException("JSON does not allow non-finite numbers.");}} else if (o instanceof Float) {if (((Float) o).isInfinite() || ((Float) o).isNaN()) {throw new JSONException("JSON does not allow non-finite numbers.");}}}}public String toString() {try {Iterator<Object> keys = mHashMap.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(mHashMap.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());}}2.使用fastjson.jar中的com.alibaba.fastjson.annotation.JSONType,用@JSONType来标注实体类import com.alibaba.fastjson.annotation.JSONType;@JSONType(orders = { "type", "content", "locationUrl"})public class MessageDto {private Sting type;private String content;private String locationUrl;public MessageDto() {}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getLocationUrl() {return locationUrl;}   public void setLocationUrl(String locationUrl) {        this.locationUrl = locationUrl;     } }使用如下:MessageDto msg = new MessageDto();msg.setContent("1");msg.setLocationUrl("2");msg.setType(3);那么com.alibaba.fastjson.JSON.toJSONString(msg),得到的就是{"type":"3","content":"1","locationUrl":"2"},这跟标注的(orders = { "type", "content", "locationUrl"})一致。

以上仅从技术角度来讨论。可能有的人会说,肯定不会有这样的需求,但我确实遇到过这样的需求。多谢。

一个人的天地是冷得连呼吸都会寂寞的颤栗,而麻烦,

【Android 一般进阶】固定JSONObject.toString()键值对顺序

相关文章:

你感兴趣的文章:

标签云: