0

0

使用RestTemplate获取并处理API数据:过滤、去重与自定义转换

聖光之護

聖光之護

发布时间:2025-09-28 10:47:47

|

498人浏览过

|

来源于php中文网

原创

使用resttemplate获取并处理api数据:过滤、去重与自定义转换

本文详细介绍了如何使用Spring RestTemplate从外部API获取数据,并在此基础上,利用Java Stream API对复杂对象列表进行高效过滤、去重以及自定义转换。通过实际代码示例,您将学习如何处理嵌套的JSON结构,移除重复项,并将数据重塑为满足自身业务需求的新格式,最终在您的Spring Boot API中展示处理后的数据。

1. 背景与问题描述

在现代应用开发中,从第三方API获取数据是常见需求。Spring框架的RestTemplate是一个强大的HTTP客户端,用于同步地进行RESTful服务调用。然而,获取到的数据往往需要进一步处理才能满足业务需求,例如过滤特定条件的数据、去除重复项,或者将原始数据结构转换为更符合内部API或前端展示的格式。

假设我们通过RestTemplate获取了一个包含省份信息的JSON数据,并将其映射到Provinces和ProvincesData等Java对象。原始数据可能包含重复的省份代码(CODPROV)或自治社区代码(CODAUTON),并且我们可能希望只保留不重复的自治社区信息,甚至将这些信息以自定义的字段名呈现。直接操作原始的List对象进行过滤和转换,需要一种高效且可读性强的方式。

2. 初始数据模型与RestTemplate配置

首先,我们定义了用于映射API响应的Java类:Provinces作为顶层容器,ProvincesData包含具体的省份信息。

// Provinces.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Provinces {

    @JsonProperty("provincial")
    private List provinces;

    public Provinces() {}

    public Provinces(List provinces) {
        this.provinces = provinces;
    }

    @JsonProperty("provincial")
    public List getProvinces() { // 注意:方法名已修正为标准Java Bean规范
        return provinces;
    }

    // 注意:原始问题中setter带有 @JsonProperty("Test"),这可能导致非预期行为。
    // 在实际开发中,如果JSON字段名为"provincial",则setter不应有不同的@JsonProperty。
    // 此处为演示目的,假设setter对应的是"provincial"或无特定注解。
    public void setProvinces(List provinces) { // 方法名已修正
        this.provinces = provinces;
    }
}
// ProvincesData.java
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class ProvincesData {

    @JsonProperty("CODPROV")
    private String codProv;

    @JsonProperty("NOMBRE_PROVINCIA")
    private String nomeProvincia;

    @JsonProperty("CODAUTON")
    private String codAuton;

    @JsonProperty("COMUNIDAD_CIUDAD_AUTONOMA")
    private String comunidadeCidadeAutonoma;

    public ProvincesData() {}

    public ProvincesData(String codProv, String nomeProvincia, String codAuton, String comunidadeCidadeAutonoma) {
        this.codProv = codProv;
        this.nomeProvincia = nomeProvincia;
        this.codAuton = codAuton;
        this.comunidadeCidadeAutonoma = comunidadeCidadeAutonoma;
    }

    @JsonProperty("CODPROV")
    public String getCodProv() {
        return codProv;
    }

    // 注意:原始问题中setter带有 @JsonProperty("Test"),此处修正为标准setter。
    public void setCodProv(String codProv) {
        this.codProv = codProv;
    }

    @JsonProperty("NOMBRE_PROVINCIA")
    public String getNomeProvincia() {
        return nomeProvincia;
    }

    public void setNomeProvincia(String nomeProvincia) {
        this.nomeProvincia = nomeProvincia;
    }

    @JsonProperty("CODAUTON")
    public String getCodAuton() {
        return codAuton;
    }

    public void setCodAuton(String codAuton) {
        this.codAuton = codAuton;
    }

    @JsonProperty("COMUNIDAD_CIUDAD_AUTONOMA")
    public String getComunidadeCidadeAutonoma() {
        return comunidadeCidadeAutonoma;
    }

    public void setComunidadeCidadeAutonoma(String comunidadeCidadeAutonoma) {
        this.comunidadeCidadeAutonoma = comunidadeCidadeAutonoma;
    }
}

接下来是RestTemplate的调用以及服务层和控制器层的基本结构:

// Templates.java (或一个工具类)
import org.springframework.web.client.RestTemplate;

public class Templates {
    public static Provinces restTemplateProvince(RestTemplate restTemplate) {
        String ProvinceCommunityURL = "https://www.el-tiempo.net/api/json/v2/provincias";
        Provinces province = restTemplate.getForObject(ProvinceCommunityURL, Provinces.class);
        return province;
    }
}

// ProvinceService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class ProvinceService {

    @Autowired
    private RestTemplate restTemplate; // 确保 RestTemplate 已在配置中定义为 Bean

    public Provinces getAllProvinces() {
        Provinces listOfProvinces = Templates.restTemplateProvince(restTemplate);
        return listOfProvinces;
    }
}

// ShowcaseController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ShowcaseController {

    @Autowired
    private ProvinceService provinceService;

    @GetMapping("/provinces")
    public Provinces getAllProvinces() {
        return provinceService.getAllProvinces();
    }
}

3. 数据过滤与去重

我们的目标是过滤掉ProvincesData列表中codAuton重复的项,只保留每个codAuton的第一个出现实例。这可以通过Java Stream API结合一个辅助集合来实现。

修改Templates.restTemplateProvince方法,在获取到原始数据后立即进行过滤:

// Templates.java (更新后的版本)
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Templates {
    public static Provinces restTemplateProvince(RestTemplate restTemplate) {
        String ProvinceCommunityURL = "https://www.el-tiempo.net/api/json/v2/provincias";
        Provinces province = restTemplate.getForObject(ProvinceCommunityURL, Provinces.class);

        // 如果获取到的数据或其内部列表为空,则直接返回
        if (province == null || province.getProvinces() == null) {
            return new Provinces(new ArrayList<>()); // 返回一个空的Provinces对象
        }

        List includedCodAutons = new ArrayList<>(); // 用于跟踪已包含的codAuton
        List filteredProvinces = province.getProvinces()
            .stream()
            .filter(p -> {
                // 如果 includedCodAutons 中已包含当前 codAuton,则过滤掉
                if (includedCodAutons.contains(p.getCodAuton())) {
                    return false;
                } else {
                    // 否则,将其添加到 includedCodAutons 并保留该元素
                    includedCodAutons.add(p.getCodAuton());
                    return true;
                }
            })
            .collect(Collectors.toList());

        // 将过滤后的列表设置回 Provinces 对象
        province.setProvinces(filteredProvinces);
        return province;
    }
}

代码解析:

  1. includedCodAutons:一个ArrayList,用于存储已经处理过的codAuton值。
  2. province.getProvinces().stream():将ProvincesData列表转换为流。
  3. .filter(p -> { ... }):对流中的每个元素应用过滤逻辑。
    • 在filter的lambda表达式中,我们检查当前ProvincesData对象的codAuton是否已存在于includedCodAutons中。
    • 如果存在,则返回false,表示该元素将被过滤掉。
    • 如果不存在,则将其codAuton添加到includedCodAutons中,并返回true,表示保留该元素。
  4. .collect(Collectors.toList()):将过滤后的流重新收集为一个新的List
  5. province.setProvinces(filteredProvinces):用过滤后的列表更新Provinces对象。

注意事项:

  • 效率考虑: 使用ArrayList.contains()进行重复检查的时间复杂度为O(n)。如果数据量非常大,使用HashSet (new HashSet()) 来存储includedCodAutons会更高效,因为HashSet.contains()的平均时间复杂度为O(1)。
  • 数据完整性: 这种过滤方式会保留每个codAuton第一次出现的ProvincesData对象。如果需要基于其他条件(如最新数据、特定属性值)选择要保留的重复项,则需要更复杂的逻辑。

4. 数据转换与自定义输出格式

仅仅过滤去重可能不足以满足所有需求。有时我们还需要将数据转换为一个全新的结构,例如只包含特定字段、字段名称不同,或者完全映射为List

HaiSnap
HaiSnap

一站式AI应用开发和部署工具

下载

假设我们希望最终API返回的数据是一个包含自治社区代码和名称的简化列表,并且字段名更具可读性。我们可以定义一个新的DTO(Data Transfer Object)来表示这种简化结构。

// SimplifiedCommunityInfo.java
public class SimplifiedCommunityInfo {
    private String communityCode;
    private String communityName;

    public SimplifiedCommunityInfo(String communityCode, String communityName) {
        this.communityCode = communityCode;
        this.communityName = communityName;
    }

    // Getters and Setters
    public String getCommunityCode() {
        return communityCode;
    }

    public void setCommunityCode(String communityCode) {
        this.communityCode = communityCode;
    }

    public String getCommunityName() {
        return communityName;
    }

    public void setCommunityName(String communityName) {
        this.communityName = communityName;
    }
}

现在,我们可以在ProvinceService中对从Templates.restTemplateProvince获取到的数据进行进一步的转换:

// ProvinceService.java (更新后的版本,包含转换逻辑)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ProvinceService {

    @Autowired
    private RestTemplate restTemplate;

    // 返回原始Provinces对象(已过滤)
    public Provinces getFilteredProvinces() {
        return Templates.restTemplateProvince(restTemplate);
    }

    // 返回自定义DTO列表
    public List getSimplifiedCommunityInfo() {
        Provinces filteredProvinces = Templates.restTemplateProvince(restTemplate);

        if (filteredProvinces == null || filteredProvinces.getProvinces() == null) {
            return List.of(); // 返回空列表
        }

        return filteredProvinces.getProvinces()
            .stream()
            .map(p -> new SimplifiedCommunityInfo(p.getCodAuton(), p.getComunidadeCidadeAutonoma()))
            .collect(Collectors.toList());
    }

    // 如果需要返回 List
    public List getCommunityNamesAsStringList() {
        Provinces filteredProvinces = Templates.restTemplateProvince(restTemplate);

        if (filteredProvinces == null || filteredProvinces.getProvinces() == null) {
            return List.of(); // 返回空列表
        }

        return filteredProvinces.getProvinces()
            .stream()
            .map(p -> p.getComunidadeCidadeAutonoma() + " (" + p.getCodAuton() + ")")
            .collect(Collectors.toList());
    }
}

相应的,ShowcaseController也需要更新以调用新的服务方法并返回期望的类型:

// ShowcaseController.java (更新后的版本)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
public class ShowcaseController {

    @Autowired
    private ProvinceService provinceService;

    @GetMapping("/provinces/filtered")
    public Provinces getFilteredProvinces() {
        return provinceService.getFilteredProvinces();
    }

    @GetMapping("/provinces/simplified")
    public List getSimplifiedCommunityInfo() {
        return provinceService.getSimplifiedCommunityInfo();
    }

    @GetMapping("/provinces/names")
    public List getCommunityNames() {
        return provinceService.getCommunityNamesAsStringList();
    }
}

代码解析:

  1. stream().map(...):这是Stream API中用于转换元素的核心操作。它接受一个Function作为参数,将流中的每个元素映射为另一种类型。
  2. 在getSimplifiedCommunityInfo方法中,我们将每个ProvincesData对象映射为一个SimplifiedCommunityInfo对象,并传入codAuton和comunidadeCidadeAutonoma作为构造参数。
  3. 在getCommunityNamesAsStringList方法中,我们将每个ProvincesData对象映射为一个格式化的字符串。

5. 总结与最佳实践

通过本文,我们学习了如何利用RestTemplate获取外部API数据,并结合Java Stream API对数据进行高效的过滤、去重和转换。

关键点总结:

  • 数据获取: RestTemplate.getForObject()是获取JSON并直接映射到Java对象的便捷方式。
  • 数据过滤与去重: 使用stream().filter()结合一个辅助集合(如ArrayList或更高效的HashSet)可以有效地实现基于特定属性的去重。
  • 数据转换: stream().map()是实现数据结构转换的关键。您可以将原始对象映射到自定义的DTO、基本类型列表或格式化字符串。
  • 职责分离: 将数据获取(Templates类)、业务逻辑(ProvinceService)和API暴露(ShowcaseController)的职责清晰分离,有助于代码的维护性和扩展性。

进一步的考虑:

  • 错误处理: 在实际应用中,RestTemplate调用需要包含异常处理机制(如try-catch块),以应对网络问题、API返回错误或数据解析失败的情况。
  • 性能优化: 对于大规模数据,除了使用HashSet进行去重外,还可以考虑并行流(parallelStream())来加速处理,但需注意并行流的线程安全问题。
  • 缓存机制: 如果API数据不经常变化,可以考虑引入缓存机制(如Spring Cache),避免每次请求都调用外部API,从而提高响应速度并减轻外部API的负载。
  • 统一响应格式: 在控制器层,通常会封装响应数据到一个统一的响应对象中,包含状态码、消息和实际数据,以提供更友好的API接口。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

779

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

722

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

727

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

394

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

444

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

428

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16860

2023.08.03

excel制作动态图表教程
excel制作动态图表教程

本专题整合了excel制作动态图表相关教程,阅读专题下面的文章了解更多详细教程。

30

2025.12.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.1万人学习

C# 教程
C# 教程

共94课时 | 5.6万人学习

Java 教程
Java 教程

共578课时 | 39.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号