转载自:http://blog.csdn.net/ben0612/article/details/44591161
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 block
- e.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 block
- e.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 block
- e.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>();
- }
-
- @Override
- public JSONObject put(String name, boolean value) throws JSONException {
- // TODO Auto-generated method stub
- return put(name, value);
- }
-
- @Override
- public JSONObject put(String name, double value) throws JSONException {
- // TODO Auto-generated method stub
- return put(name, value);
- }
-
- @Override
- public JSONObject put(String name, int value) throws JSONException {
- // TODO Auto-generated method stub
- return put(name, value);
- }
-
- @Override
- public JSONObject put(String name, long value) throws JSONException {
- // TODO Auto-generated method stub
- return 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"})
一致。
以上仅从技术角度来讨论。可能有的人会说,肯定不会有这样的需求,但我确实遇到过这样的需求。多谢。