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

Swift中如何利用DecodingError.typeMismatch关联值实现多类型解码

Swift中DecodingError.typeMismatch关联值的使用方案

问题背景

实现自定义Decodable枚举时,希望通过DecodingError.typeMismatch的关联值结合where子句,依次尝试解码三种不同类型:多边形([[CGPoint]])、多多边形([[[CGPoint]]])、点(CGPoint)。当前代码前两个case可正常运行,但添加第三个catch块时Xcode提示该块永远不会执行,且捕获到的关联值type为Double而非预期的解码目标类型,存在使用困惑。

原代码

enum FeatureGeometryCoordinates: Decodable {
    
    // cases
    case polygon([[CGPoint]])
    case multipolygon([[[CGPoint]]])
    case point(CGPoint)
    
    // init()
    init(from decoder: Decoder) throws {
        
        let container = try decoder.singleValueContainer()
        
        do {

            let polygonVal = try container.decode([[CGPoint]].self)
            self = .polygon(polygonVal)
        } catch DecodingError.typeMismatch {

            let multipolygonVal = try container.decode([[[CGPoint]]].self)
            self = .multipolygon(multipolygonVal)
        } catch DecodingError.typeMismatch {

            let pointVal = try container.decode(CGPoint.self)
            self = .point(pointVal)
        }
    }
}

解决方案

通过嵌套do-catch结构,并给DecodingError添加扩展来精确判断错误对应的预期类型,实现依次尝试解码三种类型的逻辑:

enum FeatureGeometryCoordinates: Decodable {
    case polygon([[CGPoint]])
    case multipolygon([[[CGPoint]]])
    case point(CGPoint)
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        // 先尝试解码多边形
        do {
            let polygonVal = try container.decode([[CGPoint]].self)
            self = .polygon(polygonVal)
        } catch let error as DecodingError where error.isTypeMismatch(for: [[CGPoint]].self) {
            // 多边形解码失败且是类型不匹配,尝试解码多多边形
            do {
                let multipolygonVal = try container.decode([[[CGPoint]]].self)
                self = .multipolygon(multipolygonVal)
            } catch let error as DecodingError where error.isTypeMismatch(for: [[[CGPoint]]].self) {
                // 多多边形解码失败且是类型不匹配,尝试解码点
                do {
                    let pointVal = try container.decode(CGPoint.self)
                    self = .point(pointVal)
                } catch {
                    // 点解码失败,抛出原始错误
                    throw error
                }
            } catch {
                throw error
            }
        } catch {
            throw error
        }
    }
}

// 扩展DecodingError,方便判断是否为指定类型的类型不匹配错误
extension DecodingError {
    func isTypeMismatch(for expectedType: Any.Type) -> Bool {
        guard case .typeMismatch(let expected, _) = self else {
            return false
        }
        return expected == expectedType
    }
}

关键说明

  1. 第三个catch块无法执行的原因:
    原代码中第一个catch DecodingError.typeMismatch会捕获所有类型不匹配的错误,后续的catch块没有触发机会。通过嵌套do-catch,可以在每一层解码失败后,精准进入下一层尝试。

  2. 关联值type为Double的原因:
    当解码嵌套数组类型(如[[CGPoint]])时,解码器会逐层解析JSON结构。如果实际JSON是一维数组(点的坐标),解码器在解析外层数组的元素时,会发现元素类型不符合预期(比如预期是[CGPoint]对应的数组,但实际是单个数值),此时抛出的typeMismatch关联值是内层解析的预期类型(如Double),而非外层的数组类型。通过自定义扩展isTypeMismatch(for:),可以直接判断最外层解码的预期类型,避免被内部解析的错误干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:40:29