首页 > 解决方案 > 如何忽略导致 Alamofire 响应序列化失败的“坏”字符?

问题描述

在 20 年的开发中,我总是通过在线搜索,特别是在 Stackoverflow 上找到我开发问题的所有答案。但这一次,为了我的生活,我无法找到解决方案:我正在开发一个 iOS 应用程序,它使用公共 api 来搜索公共交通的时间表。一切正常,尤其是安卓版本。但在 iOS 中,如果响应包含无效字符,则序列化过程会失败。

我正在使用 Alamofire 5.2.1、Swift 5、Xcode 11.5,API 是 Mentz 的 DIVA/EFA 服务,主要用于中欧(我认为)公共交通。

导致序列化失败的“坏”字符来自我想的编码问题,返回的 JSON 包含两个键

"opPublicCode":"�BB"

"operator":"ÖBB"

(冗余是这里的另一个问题,但没关系)。如您所见,这个德语 Ö 曾经正确表示,但一次不正确。正是这个单个字符导致 Alamofire 生成以下错误:

responseSerializationFailed(
reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed
(error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [],
debugDescription: \"The given data was not valid JSON.\", 
underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 \"Unable to convert data to string around character 7100.\" 
UserInfo={NSDebugDescription=Unable to convert data to string around character 7100.})))))

这是调用api的代码,我觉得没什么特别的:

AF.request(url)
    .responseDecodable(of: StaResponse.self) {(response: DataResponse<StaResponse, AFError> ) in
        switch response.result {
            case .success(let value):
                callback(value, nil)
            case .failure(let error):
                callback(nil, error)
            }
        }

这是 Codable 对象(我通过将它设置为 nil 来忽略“opPublicCode”的有问题的值,因为我不需要它):

struct StaDiva : Codable {
    
    let branch : String?
    let dir : String?
    let line : String?
    let network : String?
    let opCode : String?
    let operatorName : String?
    let opPublicCode : String? = nil
    let project : String?
    let stateless : String?
    let supplement : String?
    let tripCode : String?
    
    enum CodingKeys: String, CodingKey {
        case branch = "branch"
        case dir = "dir"
        case line = "line"
        case network = "network"
        case opCode = "opCode"
        case operatorName = "operator"
        case project = "project"
        case stateless = "stateless"
        case supplement = "supplement"
        case tripCode = "tripCode"
    }
}

我已经尝试了数千种方法,但都没有奏效。我设置了标题(Content-Encoding、Accept-Encoding、Accept),我尝试使用 Alamofire 4,在我的 Alamofire 请求中使用 .responseJSON 和 .responseString 而不是 .responseDecodable。我在 Codable 结构中尝试了几个选项,删除了“opPublicCode”字段,将其设置为 Data 或 Any 而不是 String。但没有运气。每次响应包含此运算符(火车的,ÖBB)的结果时,它都会失败。

我能在这里做什么?有没有办法忽略这个字符,也许通过替换它?我在使用 Alamofire 时错过了什么吗?或者有没有办法在 Codable 结构中解决这个问题(但我不这么认为,因为在尝试解码之前就发生了失败)​​。

不用说,试图让某人对 API 进行一些更改不是一种选择。

如果您需要更多信息,我当然可以提供更多详细信息。任何帮助将非常感激!提前致谢!

标签: iosjsonswiftcharacter-encodingalamofire

解决方案


使用 Alamofire 5.2,您可以使用 Alamofire 的DataPreprocessor协议在被responseDecodable. DataPreprocessor有一个要求,func preprocess(_ data: Data) throws -> Data您可以实施它来修复您的Data. 所以你会实现这样的事情:

struct RepairPreprocessor: DataPreprocessor {
    func preprocess(_ data: Data) throws -> Data {
        // Find and remove or replace bad Data.
    }
}

然后你可以使用它responseDecodable

.request(...)
.responseDecodable(of: SomeType.self, dataPreprocessor: RepairPreprocessor()) { response in 
    // Handle response.
}

这项工作是异步执行的,但鉴于您的响应的明显大小,您需要认识到这种Data解析可能对性能产生的影响。我建议将其作为 raw 操作Data,不String进行转换,以获得更好的性能。

此外,您应该向后端供应商报告错误,说明他们的响应编码错误。它必须是 UTF-8 才能正确解析。


推荐阅读