Java如何用一個統(tǒng)一結(jié)構(gòu)接收成員名稱不固定的數(shù)據(jù)
本文介紹了一種 Java 中如何用一個統(tǒng)一結(jié)構(gòu)接收成員名稱不固定的數(shù)據(jù)的方法。
背景
最近在做企業(yè)微信的內(nèi)部應(yīng)用開發(fā),遇到了一個小問題:企業(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"
}
]
}就是說,不同接口的返回框架是一樣的,都是 errcode + errmsg + 數(shù)據(jù)部分,但數(shù)據(jù)部分的成員名稱不一樣,比如上面的 department 和 userlist。
我不知道為什么這樣設(shè)計,從 Java 開發(fā)者的習(xí)慣來講,如果由我來設(shè)計,我會盡量保持接口返回的數(shù)據(jù)結(jié)構(gòu)的一致性,比如數(shù)據(jù)部分都用 data 來表示,這樣在序列化、反序列化的時候可以用一個統(tǒng)一的泛型結(jié)構(gòu)來進行。
當然這可能是企微內(nèi)部的開發(fā)語言或習(xí)慣的差異,或者其它原因,這里也無法深究,只談如何應(yīng)對。
分析
遇到這個問題后,第一反應(yīng)是用 JSON 結(jié)構(gòu)來接收,然后不同接口的數(shù)據(jù)部分用不同的 key 來讀取??梢詫崿F(xiàn),但總覺得不夠優(yōu)雅。
然后想到 GitHub 上應(yīng)該有不少開源的企微開發(fā)的封裝庫,去看看它們的實現(xiàn),說不定會有更好的方案,最終果然有收獲。
主要看了兩個庫:
前者 WxJava 知名度更高,包含的東西也更多,包含微信、企微的各種開發(fā)包的封裝。它這塊的實現(xiàn)是用我們前面提到的方法,用 JSON 結(jié)構(gòu)來接收,然后不同接口的數(shù)據(jù)用不同的 key 來讀取。
后者 wecom-sdk 是企微的開發(fā)包。它這塊的實現(xiàn)是用了一個統(tǒng)一的泛型結(jié)構(gòu)來接收數(shù)據(jù)。
以下分別截取兩個庫的兩個部門管理相關(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;
拋開 wecom-sdk 版引入了 Retrofit2 庫的支持導(dǎo)致的代碼量銳減,在返回數(shù)據(jù)的反序列化上,我也更傾向于 wecom-sdk 版的實現(xiàn)。
實現(xiàn)
那接下來我們直接參照 wecom-sdk 里的實現(xiàn)方式,寫一個泛型類,就可以用來接收企微的不同接口返回的數(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 庫里的 @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 注解里添加。
這樣,對不同的接口的封裝,我們反序列化后統(tǒng)一 getData() 就可以獲取到數(shù)據(jù)部分了,使用時不用再去操心數(shù)據(jù)部分的 key 是什么。
以上就是Java如何用一個統(tǒng)一結(jié)構(gòu)接收成員名稱不固定的數(shù)據(jù)的詳細內(nèi)容,更多關(guān)于Java統(tǒng)一結(jié)構(gòu)接收不固定數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java實現(xiàn)后臺數(shù)據(jù)顯示在前端
這篇文章主要為大家詳細介紹了java實現(xiàn)后臺數(shù)據(jù)顯示在前端,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-02-02
Spring中事務(wù)管理的四種方法(銀行轉(zhuǎn)賬為例)
這篇文章主要給大家介紹了關(guān)于Spring中事務(wù)管理的四種方法,文中是以銀行轉(zhuǎn)賬為例,通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
Java中do-while循環(huán)的使用方法及注意事項詳解
這篇文章主要介紹了Java中do-while循環(huán)的使用方法及注意事項的相關(guān)資料,在Java編程中,do-while循環(huán)是一種基本的循環(huán)控制結(jié)構(gòu),它至少執(zhí)行一次循環(huán)體,然后根據(jù)條件判斷是否繼續(xù),文中將用法介紹的非常詳細,需要的朋友可以參考下2024-10-10
Java讀取properties文件內(nèi)容的幾種方式詳解
這篇文章主要介紹了Java讀取properties文件內(nèi)容的幾種方式詳解,讀取properties配置文件在實際的開發(fā)中使用的很多,本文來介紹常用的幾種實現(xiàn)方式,需要的朋友可以參考下2023-11-11
通過mybatis-plus進行數(shù)據(jù)庫字段加解密方式
文章主要介紹了在Java開發(fā)中,從編寫處理程序(handler)到實現(xiàn)加解密工具(util),再到配置實體和字段,以及自定義MyBatis的mapper語句的全過程2026-01-01

