使用Swift 3 JSONSerialization,如果您的数据模型的一部分是完全动态的,则始终可以将反序列化的数据保留在原处,Any并让使用者处理它。
JSONSerialization
Any
使用Swift 4 Codable,我想这样做:
Codable
struct Foo : Codable { let bar: Any; }
但是我明白了
JSONPlayground.playground:4:9: note: cannot automatically synthesize 'Decodable' because 'Any' does not conform to 'Decodable' let bar: Any; ^
如果我可以实现自己的功能Decodable,那将不会是世界的尽头,但这将需要Decoder支持对的解码Any,据我所知,这不是。例如:
Decodable
Decoder
extension Foo { init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let result = try container.decode(AnyClass.self) } }
给
error: JSONPlayground.playground:4:36: error: cannot invoke 'decode' with an argument list of type '(AnyClass.Protocol)' let result = try container.decode(AnyClass.self) ^
有什么解决办法吗?
我最终不得不实现自己的类来对Any值进行编码/解码。它不是很漂亮,但是似乎可以正常工作:
class JSONAny: Codable { public let value: Any static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError { let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny") return DecodingError.typeMismatch(JSONAny.self, context) } static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError { let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny") return EncodingError.invalidValue(value, context) } static func decode(from container: SingleValueDecodingContainer) throws -> Any { if let value = try? container.decode(Bool.self) { return value } if let value = try? container.decode(Int64.self) { return value } if let value = try? container.decode(Double.self) { return value } if let value = try? container.decode(String.self) { return value } if container.decodeNil() { return JSONNull() } throw decodingError(forCodingPath: container.codingPath) } static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any { if let value = try? container.decode(Bool.self) { return value } if let value = try? container.decode(Int64.self) { return value } if let value = try? container.decode(Double.self) { return value } if let value = try? container.decode(String.self) { return value } if let value = try? container.decodeNil() { if value { return JSONNull() } } if var container = try? container.nestedUnkeyedContainer() { return try decodeArray(from: &container) } if var container = try? container.nestedContainer(keyedBy: MyCodingKey.self) { return try decodeDictionary(from: &container) } throw decodingError(forCodingPath: container.codingPath) } static func decode(from container: inout KeyedDecodingContainer, forKey key: MyCodingKey) throws -> Any { if let value = try? container.decode(Bool.self, forKey: key) { return value } if let value = try? container.decode(Int64.self, forKey: key) { return value } if let value = try? container.decode(Double.self, forKey: key) { return value } if let value = try? container.decode(String.self, forKey: key) { return value } if let value = try? container.decodeNil(forKey: key) { if value { return JSONNull() } } if var container = try? container.nestedUnkeyedContainer(forKey: key) { return try decodeArray(from: &container) } if var container = try? container.nestedContainer(keyedBy: MyCodingKey.self, forKey: key) { return try decodeDictionary(from: &container) } throw decodingError(forCodingPath: container.codingPath) } static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] { var arr: [Any] = [] while !container.isAtEnd { let value = try decode(from: &container) arr.append(value) } return arr } static func decodeDictionary(from container: inout KeyedDecodingContainer) throws -> [String: Any] { var dict = [String: Any]() for key in container.allKeys { let value = try decode(from: &container, forKey: key) dict[key.stringValue] = value } return dict } static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws { for value in array { if let value = value as? Bool { try container.encode(value) } else if let value = value as? Int64 { try container.encode(value) } else if let value = value as? Double { try container.encode(value) } else if let value = value as? String { try container.encode(value) } else if value is JSONNull { try container.encodeNil() } else if let value = value as? [Any] { var container = container.nestedUnkeyedContainer() try encode(to: &container, array: value) } else if let value = value as? [String: Any] { var container = container.nestedContainer(keyedBy: MyCodingKey.self) try encode(to: &container, dictionary: value) } else { throw encodingError(forValue: value, codingPath: container.codingPath) } } } static func encode(to container: inout KeyedEncodingContainer, dictionary: [String: Any]) throws { for (key, value) in dictionary { let key = MyCodingKey(stringValue: key)! if let value = value as? Bool { try container.encode(value, forKey: key) } else if let value = value as? Int64 { try container.encode(value, forKey: key) } else if let value = value as? Double { try container.encode(value, forKey: key) } else if let value = value as? String { try container.encode(value, forKey: key) } else if value is JSONNull { try container.encodeNil(forKey: key) } else if let value = value as? [Any] { var container = container.nestedUnkeyedContainer(forKey: key) try encode(to: &container, array: value) } else if let value = value as? [String: Any] { var container = container.nestedContainer(keyedBy: MyCodingKey.self, forKey: key) try encode(to: &container, dictionary: value) } else { throw encodingError(forValue: value, codingPath: container.codingPath) } } } static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws { if let value = value as? Bool { try container.encode(value) } else if let value = value as? Int64 { try container.encode(value) } else if let value = value as? Double { try container.encode(value) } else if let value = value as? String { try container.encode(value) } else if value is JSONNull { try container.encodeNil() } else { throw encodingError(forValue: value, codingPath: container.codingPath) } } public required init(from decoder: Decoder) throws { if var arrayContainer = try? decoder.unkeyedContainer() { self.value = try JSONAny.decodeArray(from: &arrayContainer) } else if var container = try? decoder.container(keyedBy: MyCodingKey.self) { self.value = try JSONAny.decodeDictionary(from: &container) } else { let container = try decoder.singleValueContainer() self.value = try JSONAny.decode(from: container) } } public func encode(to encoder: Encoder) throws { if let arr = self.value as? [Any] { var container = encoder.unkeyedContainer() try JSONAny.encode(to: &container, array: arr) } else if let dict = self.value as? [String: Any] { var container = encoder.container(keyedBy: MyCodingKey.self) try JSONAny.encode(to: &container, dictionary: dict) } else { var container = encoder.singleValueContainer() try JSONAny.encode(to: &container, value: self.value) } } } class JSONNull: Codable { public init() { } public required init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if !container.decodeNil() { throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull")) } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encodeNil() } } class MyCodingKey : CodingKey { let key: String required init?(intValue: Int) { return nil } required init?(stringValue: String) { key = stringValue } var intValue: Int? { return nil } var stringValue: String { return key } }