You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI项目无法通过JSON获取数据,该如何解决?

问题描述

学习SwiftUI结合JSON使用时,调用公开API始终报错:无法读取数据,因为格式不正确。

API返回的数据地址为:https://www.abgeordnetenwatch.de/api/v2/politicians

以下是我的代码:

import SwiftUI

struct ContentView: View {
    @State private var politicians: [Politician] = []
    @State private var selectedPolitician: Politician? = nil
    
    var body: some View {
        NavigationView {
            List(politicians, id: \.id) { politician in
                Button(action: {
                    self.selectedPolitician = politician
                }) {
                    Text("\(politician.firstName) \(politician.lastName)")
                }
            }
            .navigationTitle("Politicians")
            .onAppear {
                self.loadPoliticians()
            }
            
            if let politician = selectedPolitician {
                PoliticianDetailView(politician: politician)
            }
        }
    }
    
    private func loadPoliticians() {
        let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/politicians")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                do {
                    let decoder = JSONDecoder()
                    let politicians = try decoder.decode([Politician].self, from: data)
                    DispatchQueue.main.async {
                        self.politicians = politicians
                    }
                } catch {
                    print(error.localizedDescription)
                }
            }
        }.resume()
    }
}

struct PoliticianDetailView: View {
    let politician: Politician
    
    var body: some View {
        VStack {
            Text("\(politician.firstName) \(politician.lastName)")
                .font(.title)
            Text("ID: \(politician.id)")
            Text("Sex: \(politician.sex)")
        }
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct Politician: Codable, Identifiable {
    let id: Int
    let firstName: String
    let lastName: String
    let sex: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case firstName = "first_name"
        case lastName = "last_name"
        case sex
    }
}
问题原因与解决方案

核心问题

你直接尝试将API返回的JSON解析为[Politician].self(Politician数组),但实际API返回的是包含data字段的外层对象,Politician数组嵌套在data字段中。API返回的结构大致如下:

{
  "data": [
    { "id": 1, "first_name": "...", ... },
    ...
  ],
  "meta": {...},
  "links": {...}
}

修改步骤

  1. 新增外层响应模型:创建结构体匹配API的完整返回结构:
struct PoliticianResponse: Codable {
    let data: [Politician]
}
  1. 调整解析逻辑:先解析外层响应对象,再提取内部的Politician数组:
private func loadPoliticians() {
    let url = URL(string: "https://www.abgeordnetenwatch.de/api/v2/politicians")!
    URLSession.shared.dataTask(with: url) { data, response, error in
        if let data = data {
            do {
                let decoder = JSONDecoder()
                let response = try decoder.decode(PoliticianResponse.self, from: data)
                DispatchQueue.main.async {
                    self.politicians = response.data
                }
            } catch {
                // 打印完整错误信息,便于调试定位问题
                print("解析错误详情:\(error)")
            }
        }
    }.resume()
}
  1. 字段兼容性优化:部分Politician对象的sex字段可能为null,需将其改为可选类型避免解析失败:
struct Politician: Codable, Identifiable {
    let id: Int
    let firstName: String
    let lastName: String
    let sex: String? // 修改为可选类型
    
    enum CodingKeys: String, CodingKey {
        case id
        case firstName = "first_name"
        case lastName = "last_name"
        case sex
    }
}

同时在PoliticianDetailView中处理可选值:

Text("Sex: \(politician.sex ?? "未提供")")

内容的提问来源于stack exchange,提问作者Al Ko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:25:40