fastjson存在亂序的問題

現象及緣由

一般來說,在使用json數據格式時通常不須要要求數據有序。但凡事都有例外,針對查詢時序數據這樣一個場景,就必需要求服務器端返回的數據是按時間有序的,不然前端在進行數據展現時就會有問題。 項目架構以下: 前端

數據從OpenTSDB中查詢出來的時候是有序的:java

[{
    "metrc":"cpu.usage",
    "dps": {
        "123456": 12,
        "123457": 13,
        "123458": 23,
        "123459": 32
    }
}]

執行以下操做:git

JSONObject.parseArray(json)

結果查看對應的JSON數組中的map數據是亂序的,可能的結果以下:github

[{
    "metrc":"cpu.usage",
    "dps": {
        "123457": 13,
        "123456": 12,
        "123459": 32,
        "123458": 23
    }
}]

本來但願時序數據是按時間Key有序的,可是通過fastjson解析以後就會出現Key亂序。實際上,這個問題是fastjson自己的bug,詳見:https://github.com/alibaba/fastjson/issues/660json

解決辦法

以下以解析從OpenTSDB中查詢返回的時序數據爲例。數組

1.升級fastjson版本

fastjson從1.2.3版本開始,在解析json對象時能夠指定Feature.OrderedField參數,這樣解析的結果就不會亂序。服務器

public static void main(String[] args) {
    // 模擬從OpenTSDB中查詢返回的時序數據
    String str = "[{\"metric\":\"temperature\",\"tags\":{\"device_id\":\"device-12312-14\",\"dt_name\":\"dsdsdsd\"},\"aggregateTags\":[],\"dps\":{\"1538210186542\":30,\"1538210191574\":83,\"1538210196597\":41,\"1538210201624\":56,\"1538210206654\":20,\"1538210211677\":25,\"1538210216700\":54,\"1538210221740\":36,\"1538210226773\":89,\"1538210231813\":8,\"1538210236847\":34,\"1538210241882\":83,\"1538210246916\":96,\"1538210251952\":42,\"1538210257002\":6,\"1538210262038\":87,\"1538210267076\":19,\"1538210272108\":44,\"1538210277139\":84,\"1538210282176\":41,\"1538210287216\":57,\"1538210292254\":26,\"1538210297283\":64}}]";

    // 直接使用fastjson的接口實現有序解析
    JSONArray array = JSONArray.parseObject(str.getBytes(), JSONArray.class, Feature.OrderedField);
    System.out.println(array.getJSONObject(0).getJSONObject("dps").toJSONString());
}

實際上,追蹤一下fastjson的實現源碼發現,當傳遞參數Feature.OrderedField時,底層正是使用LinkedHashMap來實現Key有序的(LinkedHashMap是按插入順序排序):架構

public JSONObject(int initialCapacity, boolean ordered){
	if (ordered) {
		// 使用LinkedHashMap保證json對象的key是按照插入順序有序的
    	map = new LinkedHashMap<String, Object>(initialCapacity);
	} else {
		map = new HashMap<String, Object>(initialCapacity);
	}
}

2.手動排序

除了能夠直接經過fastjson的接口在解析時就實現有序,還能夠對解析結果進行手動排序。ide

public static void main(String[] args) {
    // 模擬從OpenTSDB中查詢返回的時序數據
    String str = "[{\"metric\":\"temperature\",\"tags\":{\"device_id\":\"device-12312-14\",\"dt_name\":\"dsdsdsd\"},\"aggregateTags\":[],\"dps\":{\"1538210186542\":30,\"1538210191574\":83,\"1538210196597\":41,\"1538210201624\":56,\"1538210206654\":20,\"1538210211677\":25,\"1538210216700\":54,\"1538210221740\":36,\"1538210226773\":89,\"1538210231813\":8,\"1538210236847\":34,\"1538210241882\":83,\"1538210246916\":96,\"1538210251952\":42,\"1538210257002\":6,\"1538210262038\":87,\"1538210267076\":19,\"1538210272108\":44,\"1538210277139\":84,\"1538210282176\":41,\"1538210287216\":57,\"1538210292254\":26,\"1538210297283\":64}}]";

    // 解析以後手動排序
    JSONArray array = JSONArray.parseArray(str);
    System.out.println(array.toJSONString());
    JSONObject json = array.getJSONObject(0);
    // 不傳遞參數Feature.OrderedField時解析獲得的json對象key是無序的,本質上是一個HashMap結構
    Map<String, Object> map = json.getJSONObject("dps").getInnerMap();
    // 經過TreeMap對Key進行排序
    map = sortMapByKey(map);
    JSONObject dps = new JSONObject();
    dps.put("dps", map);
    System.out.println(dps.toJSONString());
}

private static Map<String, Object> sortMapByKey(Map<String, Object> map) {
    if (map == null || map.isEmpty()) {
        return null;
    }
    Map<String, Object> sortMap = new TreeMap<String, Object>(new MapKeyComparator());
    sortMap.putAll(map);
    return sortMap;
}

private static class MapKeyComparator implements Comparator<String> {
    @Override
    public int compare(String str1, String str2) {
        return str1.compareTo(str2);
    }
}

【參考】 https://dzone.com/articles/hashmap-vs-treemap-vs HashMap vs. TreeMap vs. HashTable vs. LinkedHashMapurl

相關文章
相關標籤/搜索