用不起:
苹果发布Swift支撑Codable已经有一定历史年限了,为什么还用不起来,无非就是苹果的Codable太强势了,
比方模型里的界说比数据回来的json多一个key,少一个key,key的值类型不匹配(如界说为String,回来的是Int),苹果老子直接掀桌子,整个模型为nil。这。。。
并且模型的特点想要默认值,无。。。
你牛,牛到大家不知道怎样用
所以网络一边夸他Codable好用,一边真实工程开发中却还用不起来。
搞起来:
最近研讨网上有没有好用的Codable库的时分,找到了这个。2021 年了,Swift 的 JSON-Model 转化还能有什么新花样,github.com/iwill/ExCod…
经过他的封装,把苹果包装的服服帖帖。经测验,解决如下问题:
- 多一个key
- 少一个key
- key的类型不匹配的时分,自动做类型转化
- 默认值处理好。
他的模型界说能够简化为:
struct testModel: ExAutoCodable {
@ExCodable
var courseId: Int = -1
@ExCodable
var totalSectionCount: Int = -1 // 总的章节
@ExCodable
var courseImageUrl: String = ""
@ExCodable
var tudiedSectionCount: Int = 0 // 已经学习章节
}
已然他这么好,那就用起来啰喂,,,,等等,等等
界说模型这样,居然不可:
struct testModel: ExAutoCodable {
@ExCodable
var jumpParam: [String: Any]? = [:]
@ExCodable
var matchs: [Any] = []
}
苹果老子说Any不支撑Codable???转模型的时分,这个满是空,nil。
一看工程,基本每个模型的界说都有这个呀,全有Any的界说,懵逼
研讨起来:
经过研讨stackoverflow.com/questions/4…, 发现能够给Any封装一个支撑Codable的类型,比方AnyCodable这样。然后模型里边用到Any的,悉数给换成AnyCodable。
模型改为如下,运用AnyCodable
struct testModel: ExAutoCodable {
@ExCodable
var jumpParam: [String: AnyCodable]? = [:]
@ExCodable
var matchs: [AnyCodable] = []
}
AnyCodable.swift代码如下。
//
// AnyCodable.swift
//
// 由于Any不支撑Codable,但是模型里边常常会用到[String: Any]。
// 所以增加类AnyCodable,替代Any,来支撑Codable, 如:[String: AnyCodable]。
// https://stackoverflow.com/questions/48297263/how-to-use-any-in-codable-type
import Foundation
public struct AnyCodable: Decodable {
var value: Any
struct CodingKeys: CodingKey {
var stringValue: String
var intValue: Int?
init?(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
init?(stringValue: String) { self.stringValue = stringValue }
}
init(value: Any) {
self.value = value
}
public init(from decoder: Decoder) throws {
if let container = try? decoder.container(keyedBy: CodingKeys.self) {
var result = [String: Any]()
try container.allKeys.forEach { (key) throws in
result[key.stringValue] = try container.decode(AnyCodable.self, forKey: key).value
}
value = result
} else if var container = try? decoder.unkeyedContainer() {
var result = [Any]()
while !container.isAtEnd {
result.append(try container.decode(AnyCodable.self).value)
}
value = result
} else if let container = try? decoder.singleValueContainer() {
if let intVal = try? container.decode(Int.self) {
value = intVal
} else if let doubleVal = try? container.decode(Double.self) {
value = doubleVal
} else if let boolVal = try? container.decode(Bool.self) {
value = boolVal
} else if let stringVal = try? container.decode(String.self) {
value = stringVal
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
}
} else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
}
}
}
extension AnyCodable: Encodable {
public func encode(to encoder: Encoder) throws {
if let array = value as? [Any] {
var container = encoder.unkeyedContainer()
for value in array {
let decodable = AnyCodable(value: value)
try container.encode(decodable)
}
} else if let dictionary = value as? [String: Any] {
var container = encoder.container(keyedBy: CodingKeys.self)
for (key, value) in dictionary {
let codingKey = CodingKeys(stringValue: key)!
let decodable = AnyCodable(value: value)
try container.encode(decodable, forKey: codingKey)
}
} else {
var container = encoder.singleValueContainer()
if let intVal = value as? Int {
try container.encode(intVal)
} else if let doubleVal = value as? Double {
try container.encode(doubleVal)
} else if let boolVal = value as? Bool {
try container.encode(boolVal)
} else if let stringVal = value as? String {
try container.encode(stringVal)
} else {
throw EncodingError.invalidValue(value, EncodingError.Context.init(codingPath: [], debugDescription: "The value is not encodable"))
}
}
}
}
这个结合Excodable,经过测验,完美。数据转化成功。
假如模型的界说忘记了,还是界说为Any呢。 再给Excodable库里边的源码,做安全查看,修正代码如下:
public extension Encodable {
func encode(to encoder: Encoder, nonnull: Bool, throws: Bool) throws {
var mirror: Mirror! = Mirror(reflecting: self)
while mirror != nil {
for child in mirror.children where child.label != nil {
try (child.value as? EncodablePropertyWrapper)?.encode(to: encoder, label: child.label!.dropFirst(), nonnull: false, throws: false)
// 留意:Any不支撑Codable, 能够运用AnyCodable替代。
// 留意枚举类型,要支撑Codable
assert((child.value as? EncodablePropertyWrapper) != nil, "模型:\(mirror)里边的特点:\(child.label) 需要支撑 Encodable")
}
mirror = mirror.superclassMirror
}
}
}
public extension Decodable {
func decode(from decoder: Decoder, nonnull: Bool, throws: Bool) throws {
var mirror: Mirror! = Mirror(reflecting: self)
while mirror != nil {
for child in mirror.children where child.label != nil {
try (child.value as? DecodablePropertyWrapper)?.decode(from: decoder, label: child.label!.dropFirst(), nonnull: false, throws: false)
// 留意:Any不支撑Codable, 能够运用AnyCodable替代。
// 留意枚举类型,要支撑Codable
assert((child.value as? DecodablePropertyWrapper) != nil, "模型:\(mirror)里边的特点:\(child.label) 需要支撑 Decodable")
}
mirror = mirror.superclassMirror
}
}
}
嗯,这下模型假如界说为Any,能够在运行的时分报错,提示要改为AnyCodable。
能愉快的编码了。。。
不过总感觉还差点东西。
再研讨起来:
找到这个github.com/levantAJ/An…
能够完成
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
let array: [Any] = try container.decode([Any].self, forKey: key)
经过自界说[String: Any]和[Any]的解码,完成Any的Codble。
是否能够把这个合并到Excodable里边吧,然后什么都支撑了,666。
在Excodable里边提issues,作者回复有空能够弄弄。
我急用呀,那就搞起来。
花了九牛二虎,总算搞出下面兼容代码。
// Make `Any` support Codable, like: [String: Any], [Any]
fileprivate protocol EncodableAnyPropertyWrapper {
func encode<Label: StringProtocol>(to encoder: Encoder, label: Label, nonnull: Bool, throws: Bool) throws
}
extension ExCodable: EncodableAnyPropertyWrapper {
fileprivate func encode<Label: StringProtocol>(to encoder: Encoder, label: Label, nonnull: Bool, throws: Bool) throws {
if encode != nil { try encode!(encoder, wrappedValue) }
else {
let t = type(of: wrappedValue)
if let key = AnyCodingKey(stringValue: String(label)) {
if (t is [String: Any].Type || t is [String: Any?].Type || t is [String: Any]?.Type || t is [String: Any?]?.Type) {
var container = try encoder.container(keyedBy: AnyCodingKey.self)
try container.encodeIfPresent(wrappedValue as? [String: Any], forKey: key)
} else if (t is [Any].Type || t is [Any?].Type || t is [Any]?.Type || t is [Any?]?.Type) {
var container = try encoder.container(keyedBy: AnyCodingKey.self)
try container.encodeIfPresent(wrappedValue as? [Any], forKey: key)
}
}
}
}
}
fileprivate protocol DecodableAnyPropertyWrapper {
func decode<Label: StringProtocol>(from decoder: Decoder, label: Label, nonnull: Bool, throws: Bool) throws
}
extension ExCodable: DecodableAnyPropertyWrapper {
fileprivate func decode<Label: StringProtocol>(from decoder: Decoder, label: Label, nonnull: Bool, throws: Bool) throws {
if let decode = decode {
if let value = try decode(decoder) {
wrappedValue = value
}
} else {
let t = type(of: wrappedValue)
if let key = AnyCodingKey(stringValue: String(label)) {
if (t is [String: Any].Type || t is [String: Any?].Type || t is [String: Any]?.Type || t is [String: Any?]?.Type) {
let container = try decoder.container(keyedBy: AnyCodingKey.self)
if let value = try container.decodeIfPresent([String: Any].self, forKey: key) as? Value {
wrappedValue = value
}
} else if (t is [Any].Type || t is [Any?].Type || t is [Any]?.Type || t is [Any?]?.Type) {
let container = try decoder.container(keyedBy: AnyCodingKey.self)
if let value = try container.decodeIfPresent([Any].self, forKey: key) as? Value {
wrappedValue = value
}
}
}
}
}
}
再在他用的地方增加
// MARK: - Encodable & Decodable - internal
public extension Encodable {
func encode(to encoder: Encoder, nonnull: Bool, throws: Bool) throws {
var mirror: Mirror! = Mirror(reflecting: self)
while mirror != nil {
for child in mirror.children where child.label != nil {
if let wrapper = (child.value as? EncodablePropertyWrapper) {
try wrapper.encode(to: encoder, label: child.label!.dropFirst(), nonnull: false, throws: false)
} else { //增加
try (child.value as? EncodableAnyPropertyWrapper)?.encode(to: encoder, label: child.label!.dropFirst(), nonnull: false, throws: false)
}
}
mirror = mirror.superclassMirror
}
}
}
public extension Decodable {
func decode(from decoder: Decoder, nonnull: Bool, throws: Bool) throws {
var mirror: Mirror! = Mirror(reflecting: self)
while mirror != nil {
for child in mirror.children where child.label != nil {
if let wrapper = (child.value as? DecodablePropertyWrapper) {
try wrapper.decode(from: decoder, label: child.label!.dropFirst(), nonnull: false, throws: false)
} else { //增加
try (child.value as? DecodableAnyPropertyWrapper)?.decode(from: decoder, label: child.label!.dropFirst(), nonnull: false, throws: false)
}
}
mirror = mirror.superclassMirror
}
}
}
完美:
综上,总算能够让Excodable库支撑[String: Any]和[Any]的Codable了,撒花撒花。
然后模型界说这样,也能自动编解码。
struct testModel: ExAutoCodable {
@ExCodable
var jumpParam: [String: Any]? = [:]
@ExCodable
var matchs: [Any] = []
}
针对这个库的更新修正,改到这github.com/yxh265/ExCo…
也把对应的更新提交给Excodable的作者了,期待合并。 (作者iwill说,用ExCodable提供的 ExCodableDecodingTypeConverter 协议来完成是否可行。 我看了,由于Any不支撑Codable,所以要想用ExCodableDecodingTypeConverter协议,也得要大改。也期待作者出马增加这个功能。)
最终的运用方法:
引入如下:
pod 'ExCodable', :git => 'https://github.com/yxh265/ExCodable.git', :commit => '4780fb8'
模型界说:
struct TestStruct: ExAutoCodable {
@ExCodable // 字段和特点同名能够省掉字段名和括号,但 `@ExCodable` 还是没办法省掉
var int: Int = 0
@ExCodable("string", "str", "s", "nested.string") // 支撑多个 key 以及嵌套 key 能够这样写
var string: String? = nil
@ExCodable
var anyDict: [String: Any]? = nil
@ExCodable
var anyArray: [Any] = []
}
编解码:
let test = TestStruct(int: 304, string: "Not Modified", anyDict: ["1": 2, "3": "4"], anyArray: [["1": 2, "3": "4"]])
let data = try? test.encoded() as Data?
let copy1 = try? data?.decoded() as TestStruct?
let copy2 = data.map { try? TestStruct.decoded(from: $0) }
XCTAssertEqual(copy1, test)
XCTAssertEqual(copy2, test)
引用:
2021 年了,Swift 的 JSON-Model 转化还能有什么新花样
github.com/iwill/ExCod…
stackoverflow.com/questions/4…
stackoverflow.com/questions/4…
Property wrappers in Swift和Codable