Java如何用一个统一结构接收成员名称不固定的数据

 更新时间:2024年11月29日 16:09:30   作者:mzlogin  
这篇文章主要为大家详细介绍了Java如何用一个统一结构接收成员名称不固定的数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

本文介绍了一种 Java 中如何用一个统一结构接收成员名称不固定的数据的方法。

背景

最近在做企业微信的内部应用开发,遇到了一个小问题:企业微信的不同接口,返回的数据的结构不完全一样。

比如,获取部门列表接口返回的数据结构是这样的:

{
    "errcode": 0,
    "errmsg": "ok",
    "department": [
        {
            "id": 2,
            "name": "广州研发中心",
            "name_en": "RDGZ",
            "department_leader":["zhangsan","lisi"],
            "parentid": 1,
            "order": 10
        }
    ]
}

而获取部门成员接口返回的数据结构是这样的:

{
    "errcode": 0,
    "errmsg": "ok",
    "userlist": [
        {
            "userid": "zhangsan",
            "name": "张三",
            "department": [1, 2],
            "open_userid": "xxxxxx"
        }
    ]
}

就是说,不同接口的返回框架是一样的,都是 errcode + errmsg + 数据部分,但数据部分的成员名称不一样,比如上面的 department 和 userlist

我不知道为什么这样设计,从 Java 开发者的习惯来讲,如果由我来设计,我会尽量保持接口返回的数据结构的一致性,比如数据部分都用 data 来表示,这样在序列化、反序列化的时候可以用一个统一的泛型结构来进行。

当然这可能是企微内部的开发语言或习惯的差异,或者其它原因,这里也无法深究,只谈如何应对。

分析

遇到这个问题后,第一反应是用 JSON 结构来接收,然后不同接口的数据部分用不同的 key 来读取。可以实现,但总觉得不够优雅。

然后想到 GitHub 上应该有不少开源的企微开发的封装库,去看看它们的实现,说不定会有更好的方案,最终果然有收获。

主要看了两个库:

前者 WxJava 知名度更高,包含的东西也更多,包含微信、企微的各种开发包的封装。它这块的实现是用我们前面提到的方法,用 JSON 结构来接收,然后不同接口的数据用不同的 key 来读取。

后者 wecom-sdk 是企微的开发包。它这块的实现是用了一个统一的泛型结构来接收数据。

以下分别截取两个库的两个部门管理相关接口的封装代码:

WxJava 版:

github.com/binarywang/WxJava/blob/develop/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java

@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 版:

github.com/NotFound403/wecom-sdk/blob/release/wecom-sdk/src/main/java/cn/felord/api/DepartmentApi.java

@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 库的支持导致的代码量锐减,在返回数据的反序列化上,我也更倾向于 wecom-sdk 版的实现。

实现

那接下来我们直接参照 wecom-sdk 里的实现方式,写一个泛型类,就可以用来接收企微的不同接口返回的数据了:

@Data
public class WxWorkResponse<T> {

    @JsonProperty("errmsg")
    private String errMsg;

    @JsonProperty("errcode")
    private Integer errCode;

    @JsonAlias({
            "department",
            "userlist"
    })
    private T data;
}

这里面起到关键作用的是 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,可以继续在 @JsonAlias 注解里添加。

这样,对不同的接口的封装,我们反序列化后统一 getData() 就可以获取到数据部分了,使用时不用再去操心数据部分的 key 是什么。

以上就是Java如何用一个统一结构接收成员名称不固定的数据的详细内容,更多关于Java统一结构接收不固定数据的资料请关注脚本之家其它相关文章!

相关文章

  • java实现后台数据显示在前端

    java实现后台数据显示在前端

    这篇文章主要为大家详细介绍了java实现后台数据显示在前端,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • 一文带你深入了解Java8 Stream流式编程

    一文带你深入了解Java8 Stream流式编程

    在实际项目当中,若能熟练使用Java8 的Stream流特性进行开发,就比较容易写出简洁优雅的代码。本文主要就是基于实际项目常用的Stream Api流式处理总结,希望对大家有所帮助
    2023-04-04
  • Java中API的使用方法详情

    Java中API的使用方法详情

    这篇文章主要介绍了Java中API的使用方法详情,指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可,我们可以通过帮助文档来学习这些API如何使用,需要的朋友可以参考下
    2022-04-04
  • Spring中@Async用法详解及简单实例

    Spring中@Async用法详解及简单实例

    这篇文章主要介绍了Spring中@Async用法详解及简单实例的相关资料,需要的朋友可以参考下
    2017-02-02
  • Spring中事务管理的四种方法(银行转账为例)

    Spring中事务管理的四种方法(银行转账为例)

    这篇文章主要给大家介绍了关于Spring中事务管理的四种方法,文中是以银行转账为例,通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05
  • 详解Java如何给按钮添加监视器

    详解Java如何给按钮添加监视器

    这篇文章主要介绍了详解Java如何给按钮添加监视器,使用匿名对象、实现接口、实现类、Lambda表达式、注解等,需要的朋友可以参考下
    2023-04-04
  • Java中do-while循环的使用方法及注意事项详解

    Java中do-while循环的使用方法及注意事项详解

    这篇文章主要介绍了Java中do-while循环的使用方法及注意事项的相关资料,在Java编程中,do-while循环是一种基本的循环控制结构,它至少执行一次循环体,然后根据条件判断是否继续,文中将用法介绍的非常详细,需要的朋友可以参考下
    2024-10-10
  • Java读取properties文件内容的几种方式详解

    Java读取properties文件内容的几种方式详解

    这篇文章主要介绍了Java读取properties文件内容的几种方式详解,读取properties配置文件在实际的开发中使用的很多,本文来介绍常用的几种实现方式,需要的朋友可以参考下
    2023-11-11
  • 实例讲解Java 自旋锁

    实例讲解Java 自旋锁

    这篇文章主要介绍了Java 自旋锁的相关资料,帮助大家更好的理解和学习Java并发,感兴趣的朋友可以了解下
    2020-09-09
  • 通过mybatis-plus进行数据库字段加解密方式

    通过mybatis-plus进行数据库字段加解密方式

    文章主要介绍了在Java开发中,从编写处理程序(handler)到实现加解密工具(util),再到配置实体和字段,以及自定义MyBatis的mapper语句的全过程
    2026-01-01

最新评论