fastjson序列化key乱序的问题

1.2.x fastjson默认无序

对于json object,默认情况下字段是无序的,只有数组是有序的。

开发解决思路

开发服务时,使用java编程,如果需要json key是有序的,可以指定为LinkedHashMap

方法:

  1. https://github.com/alibaba/fastjson/wiki/JSONField#5-使用ordinal指定字段的顺序
  2. JSON.parseObject(json, Feature.OrderedField);

测试人员,使用Python模拟相同顺序

默认情况json object是用HashMap存储的,所以JSONObject.toString(),是依赖HashMap存储的顺序。

// JSONObject继承于JSON
public class JSONObject extends JSON implements Map<String, Object>, Cloneable, Serializable, InvocationHandler

// JSON的toJSON方法
if (javaObject instanceof Map) {
    Map<Object, Object> map = (Map<Object, Object>) javaObject;

    int size = map.size();

    Map innerMap;
    if (map instanceof LinkedHashMap) {
        innerMap = new LinkedHashMap(size);
    } else if (map instanceof TreeMap) {
        innerMap = new TreeMap();
    } else {
        innerMap = new HashMap(size);
    }

    JSONObject json = new JSONObject(innerMap);

    for (Map.Entry<Object, Object> entry : map.entrySet()) {
        Object key = entry.getKey();
        String jsonKey = TypeUtils.castToString(key);
        Object jsonValue = toJSON(entry.getValue(), config);
        json.put(jsonKey, jsonValue);
    }

    return json;
}

// HashMap resize方法,默认容量为1 << 4 = 16,装载大于0.75 * cap的数据时,扩容成原来的 2倍
// 可以看到,key的顺序,依赖于HashMap的hash方法,和新的容量
if (e.next == null)
  newTab[e.hash & (newCap - 1)] = e;

// HashMap hash方法
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

hashcode实现、unsigned_right_shitf、参考他人博客中的代码

@staticmethod
def get_hash_code(s):
    """
    Java hashcode function
    :param s:
    :return:
    """
    h = 0
    n = len(s)
    for i, c in enumerate(s):
        h = h + ord(c) * 31 ** (n - 1 - i)
    bits = 4 * 8
    return (h + 2 ** (bits - 1)) % 2 ** bits - 2 ** (bits - 1)

@staticmethod
def hashmap_size(length):
    """
    根据key的长度获取Java hashmap扩容后的容量
    :param length: 
    :return: 
    """
    max_cap = 1 << 30
    default_load_factor = 0.75
    default_initial_cap = 1 << 4  # 16
    new_cap = default_initial_cap
    load = int(default_initial_cap * default_load_factor)
    for i in range(load, max_cap, load):
        if length > i:
            new_cap *= 2
        else:
            break
    return new_cap

@staticmethod
def int_overflow(val):
    maxint = 2147483647
    if not -maxint - 1 <= val <= maxint:
        val = (val + (maxint + 1)) % (2 * (maxint + 1)) - maxint - 1
    return val

@staticmethod
def unsigned_right_shitf(n, i):
    """
    无符号右移
    """
    # 数字小于0,则转为32位无符号uint
    if n < 0:
        n = ctypes.c_uint32(n).value
    # 正常位移位数是为正数,但是为了兼容js之类的,负数就右移变成左移好了
    if i < 0:
        return -int_overflow(n << abs(i))
    # print(n)
    return int_overflow(n >> i)

@staticmethod
def hash(s):
    """
    Java hashmap hash function
    :param s:
    :return:
    """
    h = get_hash_code(s)
    return h ^ (unsigned_right_shitf(h, 16))


if __name__ == '__main__':
  # json.loads后的字典
  params = dict()
  # 参照Java HashMap排序key
  key_list = sorted(params.keys(), key=lambda k: (size - 1) & hash(k))