java Linkedhashmap fastjson序列化无法保证插入顺序

3,312 阅读1分钟

问题

当用Linkedhashmap去存储一个有序的map数据结构,然后用fastjson去转换为json字符串再转回来的时候发现顺序丢失。

代码示例:

无法保证插入顺序代码

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.util.LinkedHashMap;
import java.util.Map;


public class Main {

    public static void main(String[] args) {
        LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
        map.put(1, "A");
        map.put(3, "A");
        map.put(8, "A");
        map.put(5, "A");

        //原始顺序
        System.out.println("原始顺序:");
        for (Object o : map.entrySet()) {
            System.out.printf("%s ", o);
        }

        //fastjson
        String fJson = JSON.toJSONString(map);
        Map m = JSONObject.parseObject(fJson, Map.class);
        System.out.println();
        System.out.println("fastjson:");
        for (Object o : m.entrySet()) {
            System.out.printf("%s ", o);
        }

    }
}

输出:

原始顺序:
1=A 3=A 8=A 5=A 
fastjson:
1=A 8=A 5=A 3=A 

原因:

问题是出在fastjson的JSON.parseObject()方法上,debug代码到如下部分(版本1.1.23):

com.alibaba.fastjson.JSONObject#JSONObject()

    public JSONObject() {
        this(16, false);
    }

com.alibaba.fastjson.JSONObject#JSONObject(int, boolean)

     public JSONObject(int initialCapacity, boolean ordered) {
        if (ordered) {
            this.map = new LinkedHashMap(initialCapacity);
        } else {
            this.map = new AntiCollisionHashMap(initialCapacity);
        }

    }

由于会默认ordered = false 所以会返回一个AntiCollisionHashMap类型对象,然后用该对象去进行put赋值,返回的该map对象无法保证我们想要的顺序。

详见fastjson issue: github.com/alibaba/fas…

解决办法

方法1: 采用gson将json字符串转换为map

gson实现:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.LinkedHashMap;
import java.util.Map;


public class Main {

    public static void main(String[] args) {
        LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
        map.put(1, "A");
        map.put(3, "A");
        map.put(8, "A");
        map.put(5, "A");

        //原始顺序
        System.out.println("原始顺序:");
        for (Object o : map.entrySet()) {
            System.out.printf("%s ", o);
        }

        //gson
        Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
        String gJson = gson.toJson(map);
        Map m = gson.fromJson(gJson, Map.class);
        System.out.println();
        System.out.println("gson:");
        for (Object o : m.entrySet()) {
            System.out.printf("%s ", o);
        }

    }
}

输出

原始顺序:
1=A 3=A 8=A 5=A 
gson:
1=A 3=A 8=A 5=A

方法2: fastjson版本升级到1.2.3以上, 指定Feature.OrderedField参数

代码示例:

//fastjson
String fJson = JSON.toJSONString(map);
Map m = JSON.parseObject(fJson, Feature.OrderedField);
System.out.println();
System.out.println("fastjson:");
for (Object o : m.entrySet()) {
    System.out.printf("%s ", o);
}

输出

原始顺序:
1=A 3=A 8=A 5=A 
fastjson:
1=A 3=A 8=A 5=A

原理: 根据JSON.parseObject(fJson, Feature.OrderedField),传入的Feature.OrderedField会使ordered=true,这样返回的map对象是LinkedHashMap类型,put的时候就会保证key的顺序。

com.alibaba.fastjson.JSONObject#JSONObject(int, boolean)

    public JSONObject(int initialCapacity, boolean ordered) {
        if (ordered) {
            this.map = new LinkedHashMap(initialCapacity);
        } else {
            this.map = new HashMap(initialCapacity);
        }

    }

参考:

cloud.tencent.com/developer/a… www.cnblogs.com/hujiapeng/p…