简体   繁体   English

如何使用Swift Decodable属性使用嵌套数组解码嵌套字典?

[英]How to Decode Nested dictionary with nested arrays using Swift Decodable property?

The actual JSON,that I need to parse in swift4 is, 我需要在swift4中解析的实际JSON是,

{
    "class": {
        "semester1": [
            {
                "name": "Kal"
            },
            {
                "name": "Jack"
            },
            {
                "name": "Igor"
            }
        ],
        "subjects": [
            "English",
            "Maths"
        ]
    },
    "location": {
        "Dept": [
            "EnglishDept",
            ],
        "BlockNo": 1000
    },
    "statusTracker": {
        "googleFormsURL": "beacon.datazoom.io",
        "totalCount": 3000
    }
}

The code that I'd tried but failed to execute is, 我尝试过但执行失败的代码是

struct Class: Decodable {
    let semester: [internalComponents]
    let location: [location]
    let statusTracker: [statusTracker]
    enum CodingKeys: String, CodingKey {
        case semester = "semester1"
        case location = "location"
        case statusTracker = "statusTracker"
    }
}
struct location: Decodable {
    let Dept: [typesSubIn]
    let BlockNo: Int
}
struct statusTracker: Decodable {
    let googleFormsURL: URL
    let totalCount: Int
}


struct internalComponents: Decodable {
    let semester1: [semsIn]
    let subjects: [subjectsIn]
}
struct semsIn: Decodable {
    let nameIn: String
}
struct subjectsIn: Decodable {
    let subjects: String
}
struct Dept: Decodable {
    let Depts: String
}

I know it's completely wrong can someone give the actual format? 我知道有人可以提供实际格式是完全错误的吗? I'm actually confused with the format for "subjects".It's not compiling as a whole too. 我实际上对“主题”的格式感到困惑。它也不是整体编译的。

There are many issues. 有很多问题。

You are making a common mistake by ignoring the root object partially. 通过部分忽略根对象,您经常犯错误。

Please take a look at the JSON: On the top level there are 3 keys class , location and statusTracker . 请看一下JSON:在顶层,有3个键classlocationstatusTracker The values for all 3 keys are dictionaries, there are no arrays. 所有3个键的值都是字典,没有数组。

Since class (lowercase) is a reserved word, I'm using components . 由于class (小写)是保留字,因此我正在使用components By the way please conform to the naming convention that struct names start with a capital letter. 顺便说一句,请遵循结构名称以大写字母开头的命名约定。

struct Root : Decodable {
    let components : Class
    let location: Location
    let statusTracker: StatusTracker

    enum CodingKeys: String, CodingKey { case components = "class", location, statusTracker }
}

There are many other problems. 还有许多其他问题。 Here a consolidated version of the other structs 这里是其他结构的合并版本

struct Class: Decodable {
    let semester1: [SemsIn]
    let subjects : [String]
}

struct Location: Decodable {
    let dept : [String]
    let blockNo : Int

    enum CodingKeys: String, CodingKey { case dept = "Dept", blockNo = "BlockNo" }
}

struct SemsIn: Decodable {
    let name: String
}

struct StatusTracker: Decodable {
    let googleFormsURL: String // URL is no benefit
    let totalCount: Int
}

Now decode Root 现在解码Root

do {
    let result = try decoder.decode(Root.self, from: data)
} catch { print(error) }

It looks like you sterilize Class object in wrong way. 看起来您以错误的方式对Class对象进行了消毒。 It should looks like: 它看起来应该像这样:

struct Class: Decodable {
    let class: [internalComponents]
    let location: [location]
    let statusTracker: [statusTracker]
}

There are a few things here causing your issue. 这里有几件事引起您的问题。

  • You have no top level item, I added Response struct 您没有顶级项目,我添加了Response结构
  • Location, class and statusTracker are both at the same level, not under class. Location,class和statusTracker都处于同一级别,而不是在class之下。
  • In your class struct, your items are set as arrays but they aren't arrays 在您的类结构中,您的项目设置为数组,但不是数组
  • To debug these types of issues, wrap your decode in a do catch block and print out the error. 要调试这些类型的问题,请将解码器包装在do catch块中并打印出错误。 it will tell you the reason it failed to parse 它会告诉您解析失败的原因

Try this code from my playground: 在我的游乐场尝试以下代码:

let jsonData = """
{
    "class": {
        "semester1": [{
            "name": "Kal"
        }, {
            "name": "Jack"
        }, {
            "name": "Igor"
        }],
        "subjects": [
            "English",
            "Maths"
        ]
    },
    "location": {
        "Dept": [
            "EnglishDept"
        ],
        "BlockNo": 1000
    },
    "statusTracker": {
        "googleFormsURL": "beacon.datazoom.io",
        "totalCount": 3000
    }
}
""".data(using: .utf8)!

struct Response: Decodable {
    let cls: Class
    let location: Location
    let statusTracker: statusTracker

    enum CodingKeys: String, CodingKey {
        case cls = "class"
        case location
        case statusTracker
    }
}

struct Class: Decodable {
    let semester: [SemesterStudents]
    let subjects: [String]

    enum CodingKeys: String, CodingKey {
        case semester = "semester1"
        case subjects
    }
}

struct Location: Decodable {
    let dept: [String]
    let blockNo: Int

    enum CodingKeys: String, CodingKey {
        case dept = "Dept"
        case blockNo = "BlockNo"
    }
}

struct statusTracker: Decodable {
    let googleFormsURL: URL
    let totalCount: Int
}

struct SemesterStudents: Decodable {
    let name: String
}

struct Dept: Decodable {
    let Depts: String
}

do {
    let result = try JSONDecoder().decode(Response.self, from: jsonData)
    print(result)
} catch let error {
    print(error)
}

Another approach is to create an intermediate model that closely matches the JSON, let Swift generate the methods to decode it, and then pick off the pieces that you want in your final data model: 另一种方法是创建一个与JSON紧密匹配的中间模型,让Swift生成对它进行解码的方法,然后从最终数据模型中挑选出所需的片段:

// snake_case to match the JSON
fileprivate struct RawServerResponse: Decodable {
    struct User: Decodable {
        var user_name: String
        var real_info: UserRealInfo
    }

    struct UserRealInfo: Decodable {
        var full_name: String
    }

    struct Review: Decodable {
        var count: Int
    }

    var id: Int
    var user: User
    var reviews_count: [Review]
}

struct ServerResponse: Decodable {
    var id: String
    var username: String
    var fullName: String
    var reviewCount: Int

    init(from decoder: Decoder) throws {
        let rawResponse = try RawServerResponse(from: decoder)

        // Now you can pick items that are important to your data model,
        // conveniently decoded into a Swift structure
        id = String(rawResponse.id)
        username = rawResponse.user.user_name
        fullName = rawResponse.user.real_info.full_name
        reviewCount = rawResponse.reviews_count.first!.count
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM