RestTemplate JSON反序列化为枚举及GitHub API用户邮箱列表调用问题
RestTemplate默认依赖Jackson处理JSON序列化/反序列化,所以搞定枚举反序列化本质就是配置Jackson对枚举的映射规则,分两种常见场景处理:
场景1:JSON值和枚举名称完全匹配
这种情况完全不用额外配置,Jackson会自动对应枚举名称。比如你定义了这样的枚举:public enum UserStatus { ACTIVE, INACTIVE, SUSPENDED }如果接口返回的JSON是
{"status": "ACTIVE"},RestTemplate会直接把它转成UserStatus.ACTIVE,非常省心。场景2:JSON值和枚举名称不匹配(比如用自定义编码)
这时候需要用Jackson的@JsonValue和@JsonCreator注解手动指定映射逻辑。举个例子:public enum UserStatus { ACTIVE("A"), INACTIVE("I"), SUSPENDED("S"); private final String code; UserStatus(String code) { this.code = code; } @JsonValue // 序列化时输出这个code值 public String getCode() { return code; } @JsonCreator // 反序列化时根据code找到对应枚举实例 public static UserStatus fromCode(String code) { for (UserStatus status : values()) { if (status.code.equals(code)) { return status; } } throw new IllegalArgumentException("无效的状态码: " + code); } }这样当JSON返回
{"status": "A"}时,RestTemplate就能准确转成UserStatus.ACTIVE了。
另外,如果你的RestTemplate是自定义配置的,要确保Jackson消息转换器已经注册,否则上面的配置可能不生效:
RestTemplate restTemplate = new RestTemplate(); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(new ObjectMapper()); restTemplate.getMessageConverters().add(converter);
先看你给出的GitHubEmail类,已经加了@JsonIgnoreProperties(ignoreUnknown = true),这个做得很对——GitHub API返回的邮箱数据会包含一些我们没定义的字段,这个注解能让Jackson忽略那些未知字段,避免反序列化报错。
我先把这个类补全(原代码未完成,且Jackson反序列化需要无参构造器和getter/setter):
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.Objects; /** * {@link GitHubEmail}类表示GitHub API存储的邮箱地址(及相关附加信息)的描述。 */ @JsonIgnoreProperties(ignoreUnknown = true) public class GitHubEmail { /** * 邮箱地址。 */ private String email; /** * 主邮箱标识;表示该邮箱是否为用户的主邮箱 */ private boolean primary; /** * 邮箱是否已验证 */ private boolean verified; /** * 邮箱的可见性状态,比如"public"或"private" */ private String visibility; // Jackson反序列化必须要有无参构造器 public GitHubEmail() {} public GitHubEmail(String email, boolean primary, boolean verified, String visibility) { this.email = email; this.primary = primary; this.verified = verified; this.visibility = visibility; } // Getter和Setter方法 public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public boolean isPrimary() { return primary; } public void setPrimary(boolean primary) { this.primary = primary; } public boolean isVerified() { return verified; } public void setVerified(boolean verified) { this.verified = verified; } public String getVisibility() { return visibility; } public void setVisibility(String visibility) { this.visibility = visibility; } // 重写equals、hashCode和toString方便调试 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GitHubEmail that = (GitHubEmail) o; return primary == that.primary && verified == that.verified && Objects.equals(email, that.email) && Objects.equals(visibility, that.visibility); } @Override public int hashCode() { return Objects.hash(email, primary, verified, visibility); } @Override public String toString() { return "GitHubEmail{" + "email='" + email + '\'' + ", primary=" + primary + ", verified=" + verified + ", visibility='" + visibility + '\'' + '}'; } }
接下来是调用GitHub API的代码,注意获取用户邮箱需要授权(属于用户私有数据),得用个人访问令牌(PAT),且令牌要勾选user:email权限:
import org.springframework.web.client.RestTemplate; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import java.util.List; public class GitHubEmailClient { public static void main(String[] args) { // 替换成你自己的GitHub个人访问令牌 String githubToken = "your-personal-access-token"; RestTemplate restTemplate = new RestTemplate(); // 设置请求头,添加Bearer授权 HttpHeaders headers = new HttpHeaders(); headers.setBearerAuth(githubToken); HttpEntity<Void> requestEntity = new HttpEntity<>(headers); // 调用GitHub API,注意要用ParameterizedTypeReference处理泛型列表 String apiUrl = "https://api.github.com/user/emails"; try { List<GitHubEmail> emailList = restTemplate.exchange( apiUrl, HttpMethod.GET, requestEntity, new ParameterizedTypeReference<List<GitHubEmail>>() {} ).getBody(); // 打印获取到的邮箱列表 if (emailList != null) { emailList.forEach(System.out::println); } } catch (Exception e) { System.err.println("调用API出错了: " + e.getMessage()); e.printStackTrace(); } } }
这里有几个关键注意点:
- 授权令牌:一定要确保你的令牌有
user:email权限,否则会返回403禁止访问。 - 泛型反序列化:因为返回的是
List<GitHubEmail>,不能直接用List.class作为响应类型(否则Jackson会把每个元素转成LinkedHashMap),必须用ParameterizedTypeReference指定具体泛型类型。 - 异常处理:实际项目里要处理网络错误、API返回的错误码(比如401、404)等情况,上面的try-catch是最基础的处理方式。
内容的提问来源于stack exchange,提问作者user1902183

