使用swift 4从网站获取JSON并将其转换为对象非常简单:
class func getJSON(completionHandler: @escaping (MyModel) -> Void)
{
let myUrl = "https://example.com/whatever"
if let myUrl = URL(string: myUrl)
{
URLSession.shared.dataTask(with: myUrl)
{ (data, response, err) in
if let data = data
{
do
{
let myData = try JSONDecoder().decode(MyModel.self, from: data)
completionHandler(myData)
}
catch let JSONerr
{
print("Error: \(JSONerr)")
}
}
return
}.resume()
}
}
MyModel是一个数据模型:
struct MyModel
{
products: [MyProduct]
}
struct MyProduct
{
id: Int
...
我使用它从我的WebService获取GET,它适用于大多数JSON结构。
但是,我遇到了这个复杂的JSON对象的麻烦。 (通过复合我的意思是在这里发布太长了,所以我希望你能弄清楚这样的模式。还有JSON对象它有很多嵌套数组等)
例如。
{
"products" : [
{
"name" : "abc"
"colors" : [
{
"outside" : [
{
"main" : "blue"
}]
}]
"id" :
...
},
{
"name" : "xyzzy"
"colors" : [
{
"outside" : [
{
"main" : "blue"
}]
}]
"id" :
...
}]
}
(这可能不是有效的JSON - 它是较大部分的简单摘录。)
似乎有问题的值有时是Int,有时是String。
这不仅发生在某个键上。我知道这个JSON中至少有五个类似的案例。
这意味着如果解决方案仅针对该特定密钥,则可能会因另一个密钥而出现另一个错误。我也不会惊讶地发现很多其他案例。
问题:如何正确地将JSON解码为我的对象,其元素的类型可以是Int还是String?
我想要一个适用于所有Model成员的解决方案,或者尝试在Int失败时将值转换为String类型。因为我不知道哪个其他键会失败。
您可以使用if来处理不可预测的值:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: MemberKeys.self)
if let memberValue = try? container.decode([String].self, forKey: .member){
stringArrayMember = memberValue
}
else if let str = try? container.decode(String.self, forKey: .member){
stringMember = str
}
else if let int = try? container.decode(Int.self, forKey: .member){
intMember = int
}
}
或者,如果它是String vs Int的特定情况,并且您希望使用相同的变量来处理值,那么类似于:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: MemberKeys.self)
if let str = try? container.decode(String.self, forKey: .member){
stringMember = str
}
else if let int = try? container.decode(Int.self, forKey: .member){
stringMember = String(int)
}
}
编辑
你的MyProduct
现在看起来像:
struct MyProduct: Decodable {
var id: String?
var someOtherProperty: String?
enum MemberKeys: String, CodingKey {
case id
case someOtherProperty
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: MemberKeys.self)
someOtherProperty = try? container.decode(String.self, forKey: .someOtherProperty)
// Problematic property which can be String/Int
if let str = try? container.decode(String.self, forKey: .id){
id = str
}
else if let int = try? container.decode(Int.self, forKey: .id){
id = String(int)
}
}
}
希望这可以帮助。
这不是错误消息给出的问题!
我需要做的就是解决这个问题就是使用CodingKeys。
我希望避免这种情况,因为数据结构(JSON)有很多成员。但这解决了这个问题。
现在我的模型的一个例子:
struct Product
{
let name: String
let long_name_value: String
...
enum MemberKeys: String, CodingKey
{
case name
case longNameValue = "long_name_value"
...
}
}
我想原因是swift不喜欢蛇案例(例如“long_name_value”),所以我需要将它转换为驼峰案例(例如“longNameValue”)。然后错误就消失了。