Java如何用一個(gè)統(tǒng)一結(jié)構(gòu)接收成員名稱不固定的數(shù)據(jù)
本文介紹了一種 Java 中如何用一個(gè)統(tǒng)一結(jié)構(gòu)接收成員名稱不固定的數(shù)據(jù)的方法。
背景
最近在做企業(yè)微信的內(nèi)部應(yīng)用開(kāi)發(fā),遇到了一個(gè)小問(wèn)題:企業(yè)微信的不同接口,返回的數(shù)據(jù)的結(jié)構(gòu)不完全一樣。
比如,獲取部門列表接口返回的數(shù)據(jù)結(jié)構(gòu)是這樣的:
{ "errcode": 0, "errmsg": "ok", "department": [ { "id": 2, "name": "廣州研發(fā)中心", "name_en": "RDGZ", "department_leader":["zhangsan","lisi"], "parentid": 1, "order": 10 } ] }
而獲取部門成員接口返回的數(shù)據(jù)結(jié)構(gòu)是這樣的:
{ "errcode": 0, "errmsg": "ok", "userlist": [ { "userid": "zhangsan", "name": "張三", "department": [1, 2], "open_userid": "xxxxxx" } ] }
就是說(shuō),不同接口的返回框架是一樣的,都是 errcode + errmsg + 數(shù)據(jù)部分,但數(shù)據(jù)部分的成員名稱不一樣,比如上面的 department
和 userlist
。
我不知道為什么這樣設(shè)計(jì),從 Java 開(kāi)發(fā)者的習(xí)慣來(lái)講,如果由我來(lái)設(shè)計(jì),我會(huì)盡量保持接口返回的數(shù)據(jù)結(jié)構(gòu)的一致性,比如數(shù)據(jù)部分都用 data
來(lái)表示,這樣在序列化、反序列化的時(shí)候可以用一個(gè)統(tǒng)一的泛型結(jié)構(gòu)來(lái)進(jìn)行。
當(dāng)然這可能是企微內(nèi)部的開(kāi)發(fā)語(yǔ)言或習(xí)慣的差異,或者其它原因,這里也無(wú)法深究,只談如何應(yīng)對(duì)。
分析
遇到這個(gè)問(wèn)題后,第一反應(yīng)是用 JSON 結(jié)構(gòu)來(lái)接收,然后不同接口的數(shù)據(jù)部分用不同的 key 來(lái)讀取。可以實(shí)現(xiàn),但總覺(jué)得不夠優(yōu)雅。
然后想到 GitHub 上應(yīng)該有不少開(kāi)源的企微開(kāi)發(fā)的封裝庫(kù),去看看它們的實(shí)現(xiàn),說(shuō)不定會(huì)有更好的方案,最終果然有收獲。
主要看了兩個(gè)庫(kù):
前者 WxJava 知名度更高,包含的東西也更多,包含微信、企微的各種開(kāi)發(fā)包的封裝。它這塊的實(shí)現(xiàn)是用我們前面提到的方法,用 JSON 結(jié)構(gòu)來(lái)接收,然后不同接口的數(shù)據(jù)用不同的 key 來(lái)讀取。
后者 wecom-sdk 是企微的開(kāi)發(fā)包。它這塊的實(shí)現(xiàn)是用了一個(gè)統(tǒng)一的泛型結(jié)構(gòu)來(lái)接收數(shù)據(jù)。
以下分別截取兩個(gè)庫(kù)的兩個(gè)部門管理相關(guān)接口的封裝代碼:
WxJava 版:
@Override public List<WxCpDepart> list(Long id) throws WxErrorException { String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST); if (id != null) { url += "?id=" + id; } String responseContent = this.mainService.get(url, null); JsonObject tmpJsonObject = GsonParser.parse(responseContent); return WxCpGsonBuilder.create() .fromJson(tmpJsonObject.get("department"), new TypeToken<List<WxCpDepart>>() { }.getType() ); } @Override public List<WxCpDepart> simpleList(Long id) throws WxErrorException { String url = this.mainService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_SIMPLE_LIST); if (id != null) { url += "?id=" + id; } String responseContent = this.mainService.get(url, null); JsonObject tmpJsonObject = GsonParser.parse(responseContent); return WxCpGsonBuilder.create() .fromJson(tmpJsonObject.get("department_id"), new TypeToken<List<WxCpDepart>>() { }.getType() ); } }
wecom-sdk 版:
@GET("department/list") GenericResponse<List<DeptInfo>> deptList(@Query("id") long departmentId) throws WeComException; @GET("department/simplelist") GenericResponse<List<DeptSimpleInfo>> getSimpleList(@Query("id") long departmentId) throws WeComException;
拋開(kāi) wecom-sdk 版引入了 Retrofit2 庫(kù)的支持導(dǎo)致的代碼量銳減,在返回?cái)?shù)據(jù)的反序列化上,我也更傾向于 wecom-sdk 版的實(shí)現(xiàn)。
實(shí)現(xiàn)
那接下來(lái)我們直接參照 wecom-sdk 里的實(shí)現(xiàn)方式,寫一個(gè)泛型類,就可以用來(lái)接收企微的不同接口返回的數(shù)據(jù)了:
@Data public class WxWorkResponse<T> { @JsonProperty("errmsg") private String errMsg; @JsonProperty("errcode") private Integer errCode; @JsonAlias({ "department", "userlist" }) private T data; }
這里面起到關(guān)鍵作用的是 Jackson 庫(kù)里的 @JsonAlias
注解。它的官方文檔是這樣介紹的:
Annotation that can be used to define one or more alternative names for a property, accepted during deserialization as alternative to the official name. Alias information is also exposed during POJO introspection, but has no effect during serialization where primary name is always used. Examples: public class Info { @JsonAlias({ "n", "Name" }) public String name; } NOTE: Order of alias declaration has no effect. All properties are assigned in the order they come from incoming JSON document. If same property is assigned more than once with different value, later will remain. For example, deserializing public class Person { @JsonAlias({ "name", "fullName" }) public String name; } from { "fullName": "Faster Jackson", "name": "Jackson" } will have value "Jackson". Also, can be used with enums where incoming JSON properties may not match the defined enum values. For instance, if you have an enum called Size with values SMALL, MEDIUM, and LARGE, you can use this annotation to define alternate values for each enum value. This way, the deserialization process can map the incoming JSON values to the correct enum values. Sample implementation: public enum Size { @JsonAlias({ "small", "s", "S" }) SMALL, @JsonAlias({ "medium", "m", "M" }) MEDIUM, @JsonAlias({ "large", "l", "L" }) LARGE } During deserialization, any of these JSON structures will be valid and correctly mapped to the MEDIUM enum value: {"size": "m"}, {"size": "medium"}, or {"size": "M"}.
回到我們的例子,除了 department
和 userlist
之外還用到其它的 key,可以繼續(xù)在 @JsonAlias
注解里添加。
這樣,對(duì)不同的接口的封裝,我們反序列化后統(tǒng)一 getData()
就可以獲取到數(shù)據(jù)部分了,使用時(shí)不用再去操心數(shù)據(jù)部分的 key 是什么。
以上就是Java如何用一個(gè)統(tǒng)一結(jié)構(gòu)接收成員名稱不固定的數(shù)據(jù)的詳細(xì)內(nèi)容,更多關(guān)于Java統(tǒng)一結(jié)構(gòu)接收不固定數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java自學(xué)書籍推薦 程序員到架構(gòu)師必看的書
這篇文章主要為大家推薦了Java程序員到架構(gòu)師自學(xué)書籍,幫助大家不斷提高自己的專業(yè)水平,感興趣的小伙伴們可以參考一下2016-09-09使用CORS實(shí)現(xiàn)JavaWeb跨域請(qǐng)求問(wèn)題的方法
這篇文章主要介紹了使用Cors實(shí)現(xiàn)JavaWeb跨域請(qǐng)求問(wèn)題的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09Java中的可重入鎖ReentrantLock簡(jiǎn)析
這篇文章主要介紹了Java中的可重入鎖ReentrantLock簡(jiǎn)析,可重入是指同一個(gè)線程如果首次獲得了這把鎖,那么因?yàn)樗沁@把鎖的擁有者,因此有權(quán)利再次獲取這把鎖如果是不可重入鎖,那么第二次獲得鎖時(shí),自己也會(huì)被鎖擋住,需要的朋友可以參考下2023-12-12springboot 中 inputStream 神秘消失之謎(終破)
這篇文章主要介紹了springboot 中 inputStream 神秘消失之謎,為了能夠把這個(gè)問(wèn)題說(shuō)明,我們首先需要從簡(jiǎn)單的http調(diào)用說(shuō)起,通過(guò)設(shè)置body等一些操作,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧2021-08-08java 設(shè)計(jì)模式(DAO)的實(shí)例詳解
這篇文章主要介紹了java 設(shè)計(jì)模式(DAO)的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09