使用swift通过模型解析JSON到对象 - 解码int / string麻烦

问题描述 投票:0回答:2

使用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 - 它是较大部分的简单摘录。)


  • 该应用程序崩溃“...预计解码字符串,但发现了一个数字。”!
  • 所以我改变模型使用'String'代替'Int'。
  • 它再次崩溃,说“......期望解码Int,但发现了一个字符串/数据。”!
  • 所以我改回模型,放置'Int'代替'String'。 (循环重复。)

似乎有问题的值有时是Int,有时是String。

这不仅发生在某个键上。我知道这个JSON中至少有五个类似的案例。

这意味着如果解决方案仅针对该特定密钥,则可能会因另一个密钥而出现另一个错误。我也不会惊讶地发现很多其他案例。

问题:如何正确地将JSON解码为我的对象,其元素的类型可以是Int还是String?

我想要一个适用于所有Model成员的解决方案,或者尝试在Int失败时将值转换为String类型。因为我不知道哪个其他键会失败。

json swift string int decode
2个回答
1
投票

您可以使用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)
        }
    }
}

希望这可以帮助。


0
投票

这不是错误消息给出的问题!

我需要做的就是解决这个问题就是使用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”)。然后错误就消失了。

© www.soinside.com 2019 - 2024. All rights reserved.