首页 > Java > java教程 > 正文

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

聖光之護
发布: 2025-09-28 10:47:47
原创
483人浏览过

使用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<ProvincesData>对象进行过滤和转换,需要一种高效且可读性强的方式。

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<ProvincesData> provinces;

    public Provinces() {}

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

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

    // 注意:原始问题中setter带有 @JsonProperty("Test"),这可能导致非预期行为。
    // 在实际开发中,如果JSON字段名为"provincial",则setter不应有不同的@JsonProperty。
    // 此处为演示目的,假设setter对应的是"provincial"或无特定注解。
    public void setProvinces(List<ProvincesData> 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<String> includedCodAutons = new ArrayList<>(); // 用于跟踪已包含的codAuton
        List<ProvincesData> 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<ProvincesData>。
  5. province.setProvinces(filteredProvinces):用过滤后的列表更新Provinces对象。

注意事项:

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

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

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

降重鸟
降重鸟

要想效果好,就用降重鸟。AI改写智能降低AIGC率和重复率。

降重鸟113
查看详情 降重鸟

假设我们希望最终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<SimplifiedCommunityInfo> 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<String>
    public List<String> 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<SimplifiedCommunityInfo> getSimplifiedCommunityInfo() {
        return provinceService.getSimplifiedCommunityInfo();
    }

    @GetMapping("/provinces/names")
    public List<String> 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接口。

以上就是使用RestTemplate获取并处理API数据:过滤、去重与自定义转换的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

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