, , API Codable, ( , :)).
, :
, . , , , .
struct Character: Codable {
let name: String?
let age: Int?
let hobbies: [String]?
}
, JSON. , API Codable CodingKeys:
enum CodingKeys: String, CodingKey {
case name
case age
case hobbies
}
CodingKeys enum , - {"value":["[Ljava.lang.Object;",["Bilbo",111]]}. , SO, . - RawRepresentable, CodingKey , - String:
public extension RawRepresentable {
static var enumCases: [Self] {
var caseIndex: Int = 0
return Array(AnyIterator {
defer { caseIndex += 1 }
return withUnsafePointer(to: &caseIndex) {
$0.withMemoryRebound(to: Self.self, capacity: 1) { $0.pointee }
}
})
}
}
, , .
, Decodable, , , . , .
protocol PartiallyDecodable: Decodable {
associatedtype PartialKeys: RawRepresentable
}
Character
struct Character : Codable, PartiallyDecodable {
typealias PartialKeys = CodingKeys
- . JSONDecoder, :
enum PartialDecodingStrategy {
case singleKey(String)
case arrayOfValues
case dictionary
}
extension JSONDecoder {
func partialDecode<T>(_ type: T.Type, withStrategy strategy: PartialDecodingStrategy, from data: Data) throws -> T where T : PartiallyDecodable, T.PartialKeys.RawValue == String {
:
public extension RawRepresentable {
static var enumCases: [Self] {
var caseIndex: Int = 0
return Array(AnyIterator {
defer { caseIndex += 1 }
return withUnsafePointer(to: &caseIndex) {
$0.withMemoryRebound(to: Self.self, capacity: 1) { $0.pointee }
}
})
}
}
protocol PartiallyDecodable: Decodable {
associatedtype PartialKeys: RawRepresentable
}
enum PartialDecodingStrategy {
case singleKey(String)
case arrayOfValues
case dictionary
}
extension JSONDecoder {
func partialDecode<T>(_ type: T.Type, withStrategy strategy: PartialDecodingStrategy, from data: Data) throws -> T where T : PartiallyDecodable, T.PartialKeys.RawValue == String {
guard let partialJSON = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [AnyHashable:Any] else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "Invalid JSON"))
}
guard let value = partialJSON["value"] else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "Missing \"value\" key"))
}
let processedJSON: [AnyHashable:Any]
switch strategy {
case let .singleKey(key):
processedJSON = [key:value]
case .arrayOfValues:
guard let values = value as? [Any],
values.count == 2,
let properties = values[1] as? [Any] else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "Invalid JSON: expected a 2 elements array for the \"value\" key"))
}
processedJSON = zip(T.PartialKeys.enumCases, properties)
.reduce(into: [:]) { $0[$1.0.rawValue] = $1.1 }
case .dictionary:
guard let dict = value as? [AnyHashable:Any] else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "Invalid JSON: expected a dictionary for the \"value\" key"))
}
processedJSON = dict
}
return try decode(type, from: JSONSerialization.data(withJSONObject: processedJSON, options: []))
}
}
Character, :
struct Character: Codable, PartiallyDecodable {
typealias PartialKeys = CodingKeys
let name: String?
let age: Int?
let hobbies: [String]?
enum CodingKeys: String, CodingKey {
case name
case age
case hobbies
}
}
, :
let decoder = JSONDecoder()
let jsonData1 = "{\"value\":\"Bilbo\"}".data(using: .utf8)!
print((try? decoder.partialDecode(Character.self,
withStrategy: .singleKey(Character.CodingKeys.name.rawValue),
from: jsonData1)) as Any)
let jsonData2 = "{\"value\":[\"[Ljava.lang.Object;\",[\"Bilbo\",111]]}".data(using: .utf8)!
print((try? decoder.partialDecode(Character.self,
withStrategy: .arrayOfValues,
from: jsonData2)) as Any)
let jsonData3 = "{\"value\":{\"name\":\"Bilbo\",\"age\":111,\"hobbies\":[\"rings\"]}}".data(using: .utf8)!
print((try? decoder.partialDecode(Character.self,
withStrategy: .dictionary,
from: jsonData3)) as Any)
, :
Optional(MyApp.Character(name: Optional("Bilbo"), age: nil, hobbies: nil))
Optional(MyApp.Character(name: Optional("Bilbo"), age: Optional(111), hobbies: nil))
Optional(MyApp.Character(name: Optional("Bilbo"), age: Optional(111), hobbies: Optional(["rings"])))
, PartiallyDecodable , , . .