0

我有一个(烦人的)情况,我的后端返回一个像这样的对象:

{
"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方法,我不知道如何进行这种映射。有任何想法吗?

4

2 回答 2

2

您可以覆盖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"}
于 2017-11-21T17:50:57.837 回答
1

我倾向于使您的模型适应传入的数据并创建计算属性以在应用程序中使用,例如

struct User: Codable {
    var user: [String]
    var familyName: [String]

    var userFirstName: String? {
        return user.first
    }

    var userFamilyName: String? {
        return familyName.first
    }
}

这使您可以轻松地使用传入的数据结构维护模仿,而无需覆盖编码/解码的维护成本。

如果它与您的设计相得益彰,您还可以使用 UI 包装器 Type 或 ViewModel 来更清楚地将底层模型与其显示区分开来。

于 2017-11-21T18:12:10.563 回答