Codable 对象映射数组元素到字符串
Codable object mapping array element to string
我有一个(恼人的)情况,我的后端 returns 一个像这样的对象:
{
"user": {
"name": [
"John"
],
"familyName": [
"Johnson"
]
}
}
其中每个 属性 都是一个数组,其中包含一个字符串作为其第一个元素。在我的数据模型 struct
中,我可以将每个 属性 声明为一个数组,但这确实很难看。我想要这样的模型:
struct User: Codable {
var user: String
var familyName: String
}
但这当然会使 encoding/decoding 失败,因为类型不匹配。到现在为止,我已经使用了 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
}
}
这使您可以轻松地使用传入的数据结构维护模仿,而无需覆盖 coding/decoding 的维护成本。
如果它与您的设计相得益彰,您还可以使用 UI 包装类型或 ViewModel 来更清楚地区分底层模型和它的显示。
我有一个(恼人的)情况,我的后端 returns 一个像这样的对象:
{
"user": {
"name": [
"John"
],
"familyName": [
"Johnson"
]
}
}
其中每个 属性 都是一个数组,其中包含一个字符串作为其第一个元素。在我的数据模型 struct
中,我可以将每个 属性 声明为一个数组,但这确实很难看。我想要这样的模型:
struct User: Codable {
var user: String
var familyName: String
}
但这当然会使 encoding/decoding 失败,因为类型不匹配。到现在为止,我已经使用了 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
}
}
这使您可以轻松地使用传入的数据结构维护模仿,而无需覆盖 coding/decoding 的维护成本。
如果它与您的设计相得益彰,您还可以使用 UI 包装类型或 ViewModel 来更清楚地区分底层模型和它的显示。