java中json-diff簡單使用及對象是否一致詳解
一、摘要
今天推薦的是一款java中,對比兩個json-diff對象是否一致的工具包 json-diff` 。他可以對比任何結(jié)構(gòu)的兩個json數(shù)據(jù),并且將其中的不一致信息反饋給用戶。工具還內(nèi)置了很多配置可以來控制對比過程中的行為。目前已經(jīng)補充大量單測,穩(wěn)定性還是比較好的。
二、背景
公司最近在重構(gòu)一個核心系統(tǒng),至于為什么重構(gòu)原因很多,就不說明了。但是這個核心系統(tǒng)承載較多的線上業(yè)務(wù)。為了不影響依賴依賴該服務(wù)的應(yīng)用,所以我們重構(gòu)的最核心就是完全兼容老系統(tǒng)接口。
為了保證平滑上線,并且測試新系統(tǒng)與老系統(tǒng)是否一致,我們決定系統(tǒng)并行一段時間,并且在這段時間之中驗證新接口對老接口的兼容性。我們新起一個代理服務(wù),他會將我們的用戶流量分別轉(zhuǎn)發(fā)到新老接口,然后拿到兩個結(jié)果,將老接口結(jié)果直接返回;異步去比較新老結(jié)果是否符合預(yù)期,進行記錄或者報警。
這樣系統(tǒng)在經(jīng)過一段時間的測試,穩(wěn)定性更高,出錯的概率更小。
因為系統(tǒng)都是采用http接口對外提供服務(wù),且返回數(shù)據(jù)格式統(tǒng)一的是json格式。所以我們急需一款強大的Java語言的Json對比工具來幫助我們發(fā)現(xiàn)新老系統(tǒng)的不兼容之處。
三、工具介紹
1. 介紹
json-diff 是一款功能強大的json差異發(fā)現(xiàn)工具,支持任何結(jié)構(gòu)的json對比,并且可以將對比結(jié)果返給用戶。目前該工具更新到了 3.0.0-RC1-RELEASE
版本。最新版可以查看 版本列表 。建議使用最新版,舊版可能存在缺陷。
優(yōu)點:
- 輕量級:工具只依賴 fastjson2
- 精準(zhǔn)定位:可以返回最精準(zhǔn)且詳細(xì)的信息
- 功能全面:幾乎覆蓋任何json結(jié)構(gòu)
- 高性能
2. 使用教程
2.1 快速開始
- 引入依賴
<dependency> <groupId>cn.xiaoandcai</groupId> <artifactId>json-diff</artifactId> <!-- 舊版本可能存在某些缺陷。版本請以maven倉庫最版為準(zhǔn)。 --> <version>${version}</version> </dependency>
版本查看 2022-03-04 最新版本:3.0.0-RC1-RELEASE
- 開始使用
/** * @author: codeleep * @createTime: 2022/11/22 16:57 * @description: 使用示例 */ public class UseExample { public static void main(String[] args) { String array1 = "[1, 2, 3, 4, 5]"; String array2 = "[1, 3, 9, 4, 5]"; JsonComparedOption jsonComparedOption = new JsonComparedOption().setIgnoreOrder(true); JsonCompareResult jsonCompareResult = new DefaultJsonDifference() .option(jsonComparedOption) .detectDiff(JSON.parseArray(array1), JSON.parseArray(array2)); System.out.println(JSON.toJSONString(jsonCompareResult)); } }
結(jié)果展示:
{ "defectsList": [ { "actual": 9, "expect": 2, "illustrate": "The expect('2') data is inconsistent with the actual('9') data", "travelPath": { "abstractTravelPath": "root[]", "actualTravelPath": "root[2]", "expectTravelPath": "root[1]" } } ], "match": false }
工具會返回 match 表示是否通過比對。defectsList 則是對比信息。
2.2 更多配置
配置 | 類型 | 備注 |
---|---|---|
ignoreOrder | boolean | 是否比較過程中忽略數(shù)組順序 |
mapping | Map<String, String> | 將真實字段映射到期望字段,key是真實字段name,value是期望的字段name |
ignorePath | Set<String> | 當(dāng)對比的路徑完全匹配時會被跳過。遇到數(shù)組使用 [] 即可。無需填入下標(biāo) |
ignoreKey | Set<String> | 對比object時。或忽略該key。對整個json生效 |
customComparator | Map<String, Class<JsonNeat>> | 用戶自定義比較器。具體說明見下文 |
在 2.0.1-RC1-RELEASE
之后版本中移除了 keyFunction
配置參數(shù)??梢允褂?ignorePath
來代替達到同樣的效果。
工具提供了四個配置,來之對比過程中一些其他的要求。工具還在積極開發(fā)中,如果有新的需求,可以給作者提一個issuse。
在開發(fā)中。很多時候?qū)Ρ扰渲靡恢???梢允褂?JsonDiffOption
進行開啟唯一配置
3. 進階
3.1. 全局使用固定配置
由于在設(shè)計中考慮到各線程比較配置相互獨立。所以默認(rèn)將配置防止在 ThreadLocal
中進行存儲。但在大多數(shù)情況下,我們在全局比較時,配置并不會發(fā)生變化。
工具提供了全局配置方式。采用的方式是靜態(tài)類屬性。這樣也會獲得更好的性能。
// 開啟并設(shè)置全局配置 JsonDiffOption.openUniqueOption(); JsonDiffOption.setGloballyUniqueOption(new JsonComparedOption()); // 不想使用時可以調(diào)用調(diào)整回線程獨有模式 JsonDiffOption.closeUniqueOption();
3.2. 數(shù)組元素為對象關(guān)聯(lián)
當(dāng)我們在遇到數(shù)組元素是一個對象時。如下:
[ { "date": "23日星期五", "sunrise": "06:16", "high": "高溫 18.0℃" }, { "date": "24日星期六", "sunrise": "06:14", "high": "高溫 21.0℃" }]
在比較時, 如果希望 date
字段一致,則認(rèn)為兩個對象一致。那么可以將 sunrise
, high
字段都配置到 ignorePath
中。如:
HashSet<String> ignorePath = new HashSet<>(); ignorePath.add("root[].sunrise"); ignorePath.add("root[].high");
如果只是不想關(guān)注某個字段。即是 ignorePath
正常用法。配置如上。
3.3. 字段映射
在比較兩個對象時。也許由于字段名變更。導(dǎo)致校驗不通過。這時可以使用 mapping
配置。將 真實字段名稱映射至期望字段名稱。在比較過程中會將
actual.mappingKey 與 expect.mappingValue 認(rèn)為是應(yīng)該比較的對象。具體配置如下
// mapping key 是 actual 鍵名 // mapping value 是 expect 鍵名 HashMap<String, String> mapping = new HashMap<>(); mapping.put("date", "sunrise");
3.4. 字段忽略
如果有一些字段是想在整個json都進行忽略的,可以使用 ignoreKey
進行全局忽略。當(dāng)然如果不想全局忽略,但是配置了該項,還是會被忽略掉。
HashSet<String> ignoreKey = new HashSet<>(); ignoreKey.add("sunrise"); ignoreKey.add("high");
3.5 自定義比較器
在我們一個大json文件下??赡苡龅侥承┕?jié)點希望實現(xiàn)自定義比較??梢酝ㄟ^ customComparator
來進行實現(xiàn)。
它配置的key是一個 travelPath 。具體格式參照 ignorePath 。value 則是一個自定義比較器。對于自定義比較器需要繼承對應(yīng)的抽象類。并且實現(xiàn)具體的抽象接口。具體如下:
對象比較:
需要繼承 me.codeleep.jsondiff.core.handle.array.AbstractArrayJsonNeat
并且重寫以下方法。
/** * 比較對象 * @param expect 期望的json對象 * @param actual 實際的json對象 * @return 返回比較結(jié)果 * @throws IllegalAccessException 發(fā)生異常直接拋出 */ JsonCompareResult detectDiff(JSONObject expect, JSONObject actual);
數(shù)組比較:
需要繼承 me.codeleep.jsondiff.core.handle.object.AbstractObjectJsonNeat
并且重寫以下方法。
/** * 比較數(shù)組.調(diào)用入口。需要自己去分別調(diào)用 ignoreOrder 和 keepOrder。 * @param expect 期望的json對象 * @param actual 實際的json對象 * @return 返回比較結(jié)果 */ JsonCompareResult detectDiff(JSONArray expect, JSONArray actual); // 忽略順序的比較 JsonCompareResult ignoreOrder(JSONArray expect, JSONArray actual); // 保持順序比較 JsonCompareResult keepOrder(JSONArray expect, JSONArray actual);
基本類型比較:
基本類型指的是java基礎(chǔ)類型的包裝類型以及Number的實現(xiàn)類型。
需要繼承 me.codeleep.jsondiff.core.handle.primitive.AbstractPrimitiveJsonNeat
并且重寫以下方法。
/** * 比較數(shù)組 * @param expect 基礎(chǔ)類型對象 * @param actual 基礎(chǔ)類型對象 * @return 返回比較結(jié)果 */ JsonCompareResult detectDiff(Object expect, Object actual);
用戶可以自己根據(jù) travelPath 來決定使用何種自定義比較。三種比較器都返回 JsonCompareResult 對象作為當(dāng)前節(jié)點的比較結(jié)果。對于JsonCompareResult對象。需要填入以下信息:
// 示例 JsonCompareResult result = new JsonCompareResult(); Defects defects = new Defects() .setActual(actualDiffJson) .setExpect(expectDiffJson) .setTravelPath(nextTravelPath) .setIllustrateTemplate(DATA_TYPE_INCONSISTENT, expectDiffJson.getClass().getName(), actualDiffJson.getClass().getName()); result.addDefects(defects);
如果遇到在自定義節(jié)點中,還需要使用系統(tǒng)自帶的比較器時。
// 該值可以在上述三個抽象類中獲得。但需要經(jīng)自行處理 String abstractTravelPath = "root"; // 下一級是對象 TravelPath nextTravelPath = new TravelPath(abstractTravelPath, mappingKey); // 下一級是數(shù)組 TravelPath nextTravelPath = new TravelPath(abstractTravelPath, expectIndex, actualIndex); // 獲得比較器 JsonDiffUtil.getJsonNeat(expectDiffJson, actualDiffJson, nextTravelPath); // 執(zhí)行比較獲得結(jié)果 JsonCompareResult diff = jsonNeat.diff(expectDiffJson, actualDiffJson, nextTravelPath); // 本級創(chuàng)建的 JsonCompareResult result 將下一級結(jié)果合并 this.result.mergeDefects(diff.getDefectsList());
可以使用上述代碼獲取系統(tǒng)自帶的比較器。
自定義比較器值得注意的是: 從匹配到 travelPath 之后,根據(jù)不再接管比較操作。一切行為由用戶自行定義。但工具依然預(yù)留默認(rèn)的比較器給用戶處理后續(xù)字段。這需要用戶自行進行組合調(diào)用。
4.其他說明
前面提到工具幾乎可以支持所有json結(jié)果的對比校驗,并且發(fā)現(xiàn)差異。那它到底可以支持哪些呢,不知道是否符合你的需求呢?
對象 ?
這是最簡單的數(shù)據(jù)結(jié)構(gòu)了,其中元素都以key-value構(gòu)成,key是字符串,value可以是任何數(shù)據(jù)結(jié)構(gòu)。
數(shù)組 ?
支持嚴(yán)格順序?qū)Ρ群秃雎皂樞驅(qū)Ρ?,可以?xì)化數(shù)組元素的類型
基本類型 ?
對象類型 ?
該類型在對比時,可以通過ignorePath參數(shù)進行元素是否進行比較,將不關(guān)心的元素忽略掉。當(dāng)然ignoreKey也可以,但其是全局生效
數(shù)組類型 ?
元素也是數(shù)組,這樣就形成了多維數(shù)組,工具理論上來說支持n維數(shù)組的對比
元素類型不統(tǒng)一 ?
數(shù)組中,類型可能包含前面三種類型,這時工具會按照類型分類進行匹配,最后找不到的元素再反饋給用戶。
由于json結(jié)構(gòu)在單個看來,就只有對象和數(shù)組兩種類型,該工具完美支持了所有類型。
測試用例
目前工具測試覆蓋率已經(jīng)達到80%。剩余測試用例正在補全中,完全可用于生產(chǎn)環(huán)境。
以上就是json-diff簡單使用的詳細(xì)內(nèi)容,更多關(guān)于json-diff簡單使用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Hadoop MultipleOutputs輸出到多個文件中的實現(xiàn)方法
這篇文章主要介紹了 Hadoop MultipleOutputs輸出到多個文件中的實現(xiàn)方法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-10-10使用jib插件為Java應(yīng)用構(gòu)建鏡像的方法
這篇文章主要介紹了使用jib插件為Java應(yīng)用構(gòu)建鏡像,要是用戶本地沒安裝docker,可以使用jib制作出帶有鏡像的tar文件,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08Windows下Java+MyBatis框架+MySQL的開發(fā)環(huán)境搭建教程
這篇文章主要介紹了Windows下Java+MyBatis框架+MySQL的開發(fā)環(huán)境搭建教程,Mybatis對普通SQL語句的支持非常好,需要的朋友可以參考下2016-04-04Java Socket+mysql實現(xiàn)簡易文件上傳器的代碼
最近在做一個小項目,項目主要需求是實現(xiàn)一個文件上傳器,通過客戶端的登陸,把本地文件上傳到服務(wù)器的數(shù)據(jù)庫(本地的)。下面通過本文給大家分享下實現(xiàn)代碼,感興趣的朋友一起看看吧2016-10-10JdbcTemplate方法介紹與增刪改查操作實現(xiàn)
這篇文章主要給大家介紹了關(guān)于JdbcTemplate方法與增刪改查操作實現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用JdbcTemplate具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11java實現(xiàn)根據(jù)ip地址獲取地理位置的代碼分享
這篇文章主要介紹了java實現(xiàn)根據(jù)ip地址獲取地理位置的代碼分享,本文中使用的是QQ在線接口,也可以使用新浪、淘寶等提供的在線接口,需要的朋友可以參考下2014-08-08