【问题标题】:How to ignore a "bad" character causing Alamofire to fail with response serialization?如何忽略导致 Alamofire 响应序列化失败的“坏”字符?
【发布时间】:2020-10-20 01:20:05
【问题描述】:

在 20 年的开发过程中,我总是通过在线搜索,特别是在 Stackoverflow 上搜索来找到我开发问题的所有答案。但这一次,为了我的生命,我无法找到解决方案: 我正在开发一个使用公共 api 来搜索公共交通时间表的 iOS 应用程序。一切正常,尤其是安卓版本。但在 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 对象(我忽略了“opPublicCode”的有问题的值,将其设置为 nil,因为我不需要它):

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 进行一些更改不是一种选择。

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

【问题讨论】:

    标签: ios json swift character-encoding alamofire


    【解决方案1】:

    使用 Alamofire 5.2,您可以使用 Alamofire 的 DataPreprocessor 协议在 responseDecodable 解析之前“修复”您的有效负载。 DataPreprocessor 有一个要求 func preprocess(_ data: Data) throws -&gt; 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 解析可能产生的性能影响。我建议将其作为原始 Data 操作,不进行 String 转换,以获得更好的性能。

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

    【讨论】:

    • 非常感谢!我现在正在尝试这个,但我正在努力处理 Data 类型,因为我不知道如何确定要删除的范围。我想删除整个 opPublicCode 名称和值,因为我不需要它。也许你可以给我一个提示?再次感谢!
    • 我现在尝试通过来回转换为字符串来进行简单的查找/替换。它有效!我没有看到对性能有重大影响,但我也会尝试您推荐的方式。同时我接受你的回答!非常感谢!!
    • Data 会更难,因为它没有String 拥有的所有好的算法(当导入Foundation 时),但通常你可以找到Data 的范围形成您的搜索 String 并对其进行操作。
    猜你喜欢
    • 1970-01-01
    • 2020-06-20
    • 2020-01-06
    • 1970-01-01
    • 2014-09-11
    • 1970-01-01
    • 1970-01-01
    • 2011-04-20
    相关资源
    最近更新 更多