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 } }
关键说明
第三个catch块无法执行的原因:
原代码中第一个catch DecodingError.typeMismatch会捕获所有类型不匹配的错误,后续的catch块没有触发机会。通过嵌套do-catch,可以在每一层解码失败后,精准进入下一层尝试。关联值
type为Double的原因:
当解码嵌套数组类型(如[[CGPoint]])时,解码器会逐层解析JSON结构。如果实际JSON是一维数组(点的坐标),解码器在解析外层数组的元素时,会发现元素类型不符合预期(比如预期是[CGPoint]对应的数组,但实际是单个数值),此时抛出的typeMismatch关联值是内层解析的预期类型(如Double),而非外层的数组类型。通过自定义扩展isTypeMismatch(for:),可以直接判断最外层解码的预期类型,避免被内部解析的错误干扰。
内容的提问来源于stack exchange,提问作者whaleshark
相关产品推荐
相关产品推荐

