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

如何使用dataTaskPublisher处理接口返回的字符串、JSON等多类型数据

你原先的代码报错是因为JSONDecoder解码String类型时,要求输入必须是符合JSON规范的字符串(即内容带双引号,比如"\"hello, world!\""),而你的服务端返回的是没有包裹双引号的纯文本,不符合JSON格式要求所以解码失败。

解决方案

核心思路是扩展Combine的Publisher,针对Data类型的输出编写通用解码方法,根据目标解码类型自动选择适配的解码逻辑,不需要上层调用区分接口返回类型。

1. 编写通用解码扩展

import Combine

extension Publisher where Output == Data, Failure == URLError {
    func universalDecode<T: Decodable>(_ type: T.Type, decoder: JSONDecoder = JSONDecoder()) -> AnyPublisher<T, Error> {
        return tryMap { data in
            // 处理纯字符串返回场景
            if T.self == String.self {
                guard let result = String(data: data, encoding: .utf8) as? T else {
                    throw DecodingError.dataCorrupted(.init(
                        codingPath: [],
                        debugDescription: "Failed to convert data to UTF-8 string"
                    ))
                }
                return result
            }
            // 处理纯数值返回场景(Int)
            if T.self == Int.self {
                guard let str = String(data: data, encoding: .utf8), let result = Int(str) as? T else {
                    throw DecodingError.dataCorrupted(.init(
                        codingPath: [],
                        debugDescription: "Failed to convert data to Int"
                    ))
                }
                return result
            }
            // 处理纯数值返回场景(Double,可按需扩展其他数值类型)
            if T.self == Double.self {
                guard let str = String(data: data, encoding: .utf8), let result = Double(str) as? T else {
                    throw DecodingError.dataCorrupted(.init(
                        codingPath: [],
                        debugDescription: "Failed to convert data to Double"
                    ))
                }
                return result
            }
            // 其他Codable类型(自定义模型、数组等)走JSONDecoder解码
            return try decoder.decode(T.self, from: data)
        }
        .eraseToAnyPublisher()
    }
}

2. 统一调用示例

所有接口都可以使用同一种调用格式,只需要传入期望的返回类型即可:

调用返回纯字符串的接口

var cancellables = Set<AnyCancellable>()

URLSession
    .shared
    .dataTaskPublisher(for: stringRequest)
    .map(\.data)
    .universalDecode(String.self)
    .receive(on: DispatchQueue.main)
    .sink(
        receiveCompletion: { completion in
            // 处理错误/完成逻辑
        },
        receiveValue: { text in
            print(text) // 输出 hello, world!
        }
    )
    .store(in: &cancellables)

调用返回JSON模型的接口

// 定义业务模型
struct User: Codable {
    let id: Int
    let nickname: String
}

URLSession
    .shared
    .dataTaskPublisher(for: userRequest)
    .map(\.data)
    .universalDecode(User.self)
    .receive(on: DispatchQueue.main)
    .sink(
        receiveCompletion: { _ in },
        receiveValue: { user in
            print(user.nickname)
        }
    )
    .store(in: &cancellables)

调用返回Codable数组的接口

URLSession
    .shared
    .dataTaskPublisher(for: listRequest)
    .map(\.data)
    .universalDecode([User].self)
    .receive(on: DispatchQueue.main)
    .sink(
        receiveCompletion: { _ in },
        receiveValue: { userList in
            print(userList.count)
        }
    )
    .store(in: &cancellables)

扩展说明

如果后续需要适配其他特殊返回格式,只需要在universalDecode方法中新增对应类型的处理分支即可,不需要修改上层的调用逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 02:00:05