数据来源:陈同学 | 异构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");
总结
本文仅提取了异构数据处理中的一个“点”做分析,为数据解析提供一种解决的思路。