數據來源:陳同學 | 異構API數據處理實踐
首先用下圖闡述業務場景。
基礎服務爲各個業務服務(服務A/B/C) 提供API,同時基礎服務數據又來源於第三方服務商。
各個服務商之間API的數據結構不同,本文不涉及不同服務商之間的安全通訊方式。
爲什麼需要多個服務商?
例舉我所遇到的兩個因素
系統穩定性考慮
以發送短信爲例,若只有一個服務商,若服務商因某些因素中斷服務,將導致依賴於短信的業務受到嚴重影響。
若對接了多個服務商,當其中一個無法使用,自動切換到可用的服務商即可。
切換服務商
因各種因素導致商務合作終止,從而切換服務商
異構數據的場景舉例
先舉兩個例子加以說明:
簡單數據異構場景
假設通過企查查、天眼查的API獲取工商信息,對於企業名稱字段,企業可能分別名稱是
ENTNAME
、org_name
,有的甚至是中文字段名企業名稱
複雜數據異構場景
假設對接企業ERP中財務數據,A企業可能是金蝶系統、B企業是用友系統、其他企業可能是Oracle EBS或SAP系統。
這種場景不僅需要將異構數據處理成統一結構,而且處理過程中需要進行復雜的數據轉換過程。
異構數據處理簡單Demo
數據處理的目的是可以通過配置,將不同服務商的異構數據統一解析,簡化代碼,增強拓展性。
這裏以企業工商數據做演示。下面假設三種工商信息的數據結構,均使用JSON格式展示:
數據結構示例
- 自身標準數據結構
業務系統中以自身的數據結構爲準,假設字段名稱是正常翻譯:
{
"organizationName":"企業名稱",
"taxpayerNumber":"納稅人識別號"
}
- A服務商API返回的數據結構
假設字段名稱是不規則簡寫:
{
"ENTNAME":"企業名稱",
"TAXNUMBER":"納稅人識別號"
}
- B服務商API返回的數據結構
假設字段名稱是中文首字母簡寫:
{
"QYMC":"企業名稱",
"NSRSBH":"納稅人識別號"
}
簡單處理示例
先採用簡單的方式處理,首先新建一個Domain表示企業工商信息:
public class Organization {
private String organizationName; //企業名稱
private String taxpayerNumber; //納稅人識別號
}
將A服務商的數據轉換爲標準數據
假設JSONObject是阿里的fastjson
// JSONObject data = {"ENTNAME":"企業名稱", "TAXNUMBER":"納稅人識別號"}
Organization org = new Organization();
org.setOrganizationName(data.getString("ENTNAME"));
org.setTaxpayerNumber(data.getString("TAXNUMBER"));
將B服務商的數據轉換爲標準數據
// JSONObject data = {"QYMC":"企業名稱", "NSRSBH":"納稅人識別號"}
Organization org = new Organization();
org.setOrganizationName(data.getString("QYMC"));
org.setTaxpayerNumber(data.getString("NSRSBH"));
上面看上去非常簡單,但實際上API字段非常繁多,首先會導致大量的累贅代碼,其次是有N個服務商就會有N種冗餘代碼。
統一解析處理示例
使用註解在Domain上標記各個服務商的對應字段
定義用於數據自動轉換的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FiledMapper {
String serviceA() default ""; // A服務商字段
String serviceB() default ""; // B服務商字段
}
使用註解標記Domain的屬性
使用註解將異構數據的字段名與標準字段建立Mapping關係
public class Organization {
@FiledMapper(serviceA="ENTNAME", serviceB="QYMC")
private String organizationName; //企業名稱
@FiledMapper(serviceA="TAXNUMBER", serviceB="NSRSBH")
private String taxpayerNumber; //納稅人識別號
}
使用反射統一解析數據
/**
* 統一解析數據
*
* @param source 數據源
* @param targetClass 目標類
* @param serviceProvider 服務提供商
* @return 目標類instance
* @throws Exception
*/
public static Object parse(JSONObject source, Class targetClass, String serviceProvider) throws Exception {
Object instance = targetClass.newInstance();
Field[] fields = targetClass.getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
if (field.isAnnotationPresent(FiledMapper.class)) {
FiledMapper filedMapper = field.getAnnotation(FiledMapper.class);
field.setAccessible(true);
field.set(instance, source.get("A".equals(serviceProvider) ? filedMapper.serviceA() : filedMapper.serviceB())); // 此處hardcode做演示
}
}
}
return instance;
}
數據處理測試
JSONObject dataA = JSON.parseObject("{\"ENTNAME\":\"企業A\", \"TAXNUMBER\":\"1001\"}");
JSONObject dataB = JSON.parseObject("{\"QYMC\":\"企業A\", \"NSRSBH\":\"1001\"}");
Organization orgA = (Organization) parse(dataA, Organization.class, "A");
Organization orgB = (Organization) parse(dataB, Organization.class, "B");
總結
本文僅提取了異構數據處理中的一個“點”做分析,爲數據解析提供一種解決的思路。