我有一个(烦人的)情况,我的后端返回一个这样的对象:
{
"user": {
"name": [
"John"
],
"familyName": [
"Johnson"
]
}
}
其中每个属性都是一个数组,该数组将字符串作为其第一个元素。在我的数据模型中struct
我可以将每个属性声明为数组,但这确实很丑陋。我想让我的模型是这样的:
struct User: Codable {
var user: String
var familyName: String
}
但这当然会使编码/解码失败,因为类型不匹配。到目前为止,我一直使用ObjectMapper
库,它提供了一个Map
对象和currentValue
属性,这样我就可以将我的属性声明为String
类型,并在我的模型中init
方法通过这个函数确定每个值:
extension Map {
public func firstFromArray<T>(key: String) -> T? {
if let array = self[key].currentValue as? [T] {
return array.first
}
return self[key].currentValue as? T
}
}
但是现在我正在转换为Codable
方法,我不知道如何进行此类映射。有什么想法吗?
您可以覆盖init(from decoder: Decoder)
:
let json = """
{
"user": {
"name": [
"John"
],
"familyName": [
"Johnson"
]
}
}
"""
struct User: Codable {
var name: String
var familyName: String
init(from decoder: Decoder) throws {
let container:KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self)
let nameArray = try container.decode([String].self, forKey: .name)
let familyNameArray = try container.decode([String].self, forKey: .familyName)
self.name = nameArray.first!
self.familyName = familyNameArray.first!
}
enum CodingKeys: String, CodingKey {
case name
case familyName
}
}
let data = json.data(using: .utf8)!
let decodedDictionary = try JSONDecoder().decode(Dictionary<String, User>.self, from: data)
print(decodedDictionary) // ["user": __lldb_expr_48.User(name: "John", familyName: "Johnson")]
let encodedData = try JSONEncoder().encode(decodedDictionary["user"]!)
let encodedStr = String(data: encodedData, encoding: .utf8)
print(encodedStr!) // {"name":"John","familyName":"Johnson"}
我倾向于使您的模型适应传入的数据并创建用于应用程序的计算属性,例如
struct User: Codable {
var user: [String]
var familyName: [String]
var userFirstName: String? {
return user.first
}
var userFamilyName: String? {
return familyName.first
}
}
这使您可以轻松地维护数据结构的模仿,而无需覆盖编码/解码的维护成本。
如果它与您的设计相得益彰,您还可以使用UI包装器类型或视图模型,以更清楚地将基础模型与其显示区分开来。