如何解码 JSON 字典?

huangapple go评论101阅读模式
英文:

How to decode a JSON Dictionary?

问题

我能够解码简单的JSON响应,但随着JSON的嵌套程度增加,我感到困难。

要解码这样的JSON:

  1. [
  2. {
  3. "id": "string",
  4. "username": "string",
  5. "firstName": "string",
  6. "lastName": "string",
  7. "fullName": "string",
  8. "email": "string",
  9. "isInActivity": true,
  10. "activeEventId": "string",
  11. "initials": "string"
  12. }
  13. ]

我的结构体如下:

  1. struct FriendsStruct: Decodable, Hashable {
  2. var initials: String
  3. var username: String
  4. var firstName: String
  5. var lastName: String
  6. var fullName: String
  7. var email: String
  8. var isInActivity: Bool
  9. var activeEventId: String
  10. var id: String
  11. }

解码的方式如下:

  1. func getFriends(token: String, force: Bool) async throws -> Int {
  2. var request = EndPoints().getFriendsEndPoint(force: force)
  3. request.httpMethod = "GET"
  4. request.addValue("application/json", forHTTPHeaderField: "Content-Type")
  5. request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
  6. let (data, response) = try await URLSession.shared.data(for: request)
  7. let httpResponse = response as? HTTPURLResponse
  8. guard (response as? HTTPURLResponse)?.statusCode == 200 else { return httpResponse?.statusCode ?? 0 }
  9. let decodedFriends = try JSONDecoder().decode([FriendsStruct].self, from: data)
  10. self.updateCoreDataFriendsRecords(friendsData: decodedFriends)
  11. return httpResponse?.statusCode ?? 1000
  12. }

有人能否指导我如何使用相同的方法解码嵌套响应,例如:

  1. {
  2. "members": [
  3. {
  4. "memberId": "string",
  5. "memberName": "string",
  6. "memberUsername": "string",
  7. "memberType": 0,
  8. "isMyFriend": true,
  9. "initials": "string"
  10. }
  11. ],
  12. "name": "string",
  13. "owner": "string",
  14. "description": "string",
  15. "groupType": 0,
  16. "expiryDate": "2023-02-06T20:00:03.834Z",
  17. "readOnly": true,
  18. "isDeleted": true,
  19. "approvalRequired": true,
  20. "joinWithCode": true,
  21. "numberOfMembers": 0,
  22. "groupAssociation": 0,
  23. "id": "string",
  24. "etag": "string"
  25. }
英文:

I am able to decode straight forward json responses but as the JSON gets more nested, I'm struggling.

To decode JSON that looks like this:

  1. [
  2. {
  3. "id": "string",
  4. "username": "string",
  5. "firstName": "string",
  6. "lastName": "string",
  7. "fullName": "string",
  8. "email": "string",
  9. "isInActivity": true,
  10. "activeEventId": "string",
  11. "initials": "string"
  12. }
  13. ]

My struct is:

  1. struct FriendsStruct: Decodable, Hashable {
  2. var initials: String
  3. var username: String
  4. var firstName: String
  5. var lastName: String
  6. var fullName: String
  7. var email: String
  8. var isInActivity: Bool
  9. var activeEventId: String
  10. var id: String
  11. }

And to decode:

  1. func getFriends(token: String, force: Bool) async throws -> Int {
  2. var request = EndPoints().getFriendsEndPoint(force: force)
  3. request.httpMethod = "GET"
  4. request.addValue("application/json", forHTTPHeaderField: "Content-Type")
  5. request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
  6. let (data, response) = try await URLSession.shared.data(for: request)
  7. let httpResponse = response as? HTTPURLResponse
  8. guard (response as? HTTPURLResponse)?.statusCode == 200 else {return httpResponse?.statusCode ?? 0}
  9. let decodedFriends = try JSONDecoder().decode([FriendsStruct].self, from: data)
  10. self.updateCoreDataFriendsRecords(friendsData: decodedFriends)
  11. return httpResponse?.statusCode ?? 1000
  12. }

Can someone advise as to how I might decode with the same approach but with a nested response such as:

  1. {
  2. "members": [
  3. {
  4. "memberId": "string",
  5. "memberName": "string",
  6. "memberUsername": "string",
  7. "memberType": 0,
  8. "isMyFriend": true,
  9. "initials": "string"
  10. }
  11. ],
  12. "name": "string",
  13. "owner": "string",
  14. "description": "string",
  15. "groupType": 0,
  16. "expiryDate": "2023-02-06T20:00:03.834Z",
  17. "readOnly": true,
  18. "isDeleted": true,
  19. "approvalRequired": true,
  20. "joinWithCode": true,
  21. "numberOfMembers": 0,
  22. "groupAssociation": 0,
  23. "id": "string",
  24. "etag": "string"
  25. }

答案1

得分: 2

你需要两个结构体:一个是Member结构体,其中包含了与JSON中相同的属性(就像你在简单示例中所做的那样),另一个是外层的结构体,用于包含一个Member数组的属性。例如:

  1. struct Member: Decodable {
  2. let memberId: String
  3. let memberName: String
  4. let memberUsername: String
  5. let memberType: Int
  6. let isMyFriend: Bool
  7. let initials: String
  8. }
  9. struct Group: Decodable {
  10. let members: [Member]
  11. let name: String
  12. let owner: String
  13. let description: String
  14. let groupType: Int
  15. let expiryDate: String
  16. let readOnly: Bool
  17. let isDeleted: Bool
  18. let approvalRequired: Bool
  19. let joinWithCode: Bool
  20. let numberOfMembers: Int
  21. let groupAssociation: Int
  22. let id: String
  23. let etag: String
  24. }

这将按照JSON中的数据结构来处理数据。你可能还想进一步使用CodingKeys枚举将一些JSON字段映射到更适合的属性名称,根据需要,将expiryDate使用Date进行解码。

编辑

作为后续步骤,我提到将expiryDate字段解码为Date属性可能是下一步。回答中似乎忽略了这一点,因为它似乎是一个.iso8601日期格式,如果是这样,那么只需相应地设置解码器上的日期解码策略:

  1. let decoder = JSONDecoder()
  2. decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601)
  3. let group = decoder.decode(Group.self, from: json.data(using: .utf8)!) // 强制解包仅用于简洁性。不建议这样做 ;-)

然而,这不会生效,因为日期字段包含了小数秒,而Swift的解码器仅支持整秒。这使得它更加有趣 如何解码 JSON 字典? 因此,您需要定义一个自定义解码器:

  1. extension DateFormatter {
  2. static let iso8601WithFractionalSeconds: DateFormatter = {
  3. let formatter = DateFormatter()
  4. formatter.locale = Locale(identifier: "en_US_POSIX")
  5. formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
  6. formatter.calendar = Calendar(identifier: .iso8601)
  7. formatter.timeZone = TimeZone(secondsFromGMT: 0)
  8. return formatter
  9. }()
  10. }

然后,您可以在解码器内将expiryDate字符串解码为Date。将到期日期字段更改为Date并按照以下方式解码:

  1. struct Group: Decodable {
  2. //...
  3. let expiryDate: Date
  4. //...
  5. }
  6. let decoder = JSONDecoder()
  7. decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601WithFractionalSeconds)
  8. let group = try! decoder.decode(Group.self, from: data)
英文:

You need two structs: a Member struct with properties in the JSON (as you did for the simple example) and an outer level struct for the base data that includes a property which is an array of Member. For example:

  1. struct Member: Decodable {
  2. let memberId: String
  3. let memberName: String
  4. let memberUsername: String
  5. let memberType: Int
  6. let isMyFriend: Bool
  7. let initials: String
  8. }
  9. struct Group: Decodable {
  10. let members: [Member]
  11. let name: String
  12. let owner: String
  13. let description: String
  14. let groupType: Int
  15. let expiryDate: String
  16. let readOnly: Bool
  17. let isDeleted: Bool
  18. let approvalRequired: Bool
  19. let joinWithCode: Bool
  20. let numberOfMembers: Int
  21. let groupAssociation: Int
  22. let id: String
  23. let etag: String
  24. }

This is treating the data exactly as it is in the JSON. You'd probably want to go further and use a CodingKeys enum to map some of the json fields onto more suitable property names, and maybe, depending on needs, use a Date for the expiry date and decode the date string.

EDIT

As a follow up I mentioned the next step might be decoding the expiryDate field into a Date property. The answer passed over this as it appeared to be an .iso8601 date format, in which case all that is required is to set the date decoding strategy on the decoder accordingly:

  1. let decoder = JSONDecoder()
  2. decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601)
  3. let group = decoder.decode(Group.self, from: json.data(using: .utf8)!) // force unwrapping for brevity. Don't ;-)

However this won't work as the date field contains fractional seconds and Swift's decoder only supports whole seconds. This makes it a bit more interesting 如何解码 JSON 字典? as you'll need to define a custom decoder:

  1. extension DateFormatter {
  2. static let iso8601WithFractionalSeconds: DateFormatter = {
  3. let formatter = DateFormatter()
  4. formatter.locale = Locale(identifier: "en_US_POSIX")
  5. formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
  6. formatter.calendar = Calendar(identifier: .iso8601)
  7. formatter.timeZone = TimeZone(secondsFromGMT: 0)
  8. return formatter
  9. }()
  10. }

This then lets you decoder the expiryDate string to a Date within the decoder. Change the expiry date field to a Date and decode as below.

  1. struct Group: Decodable {
  2. //...
  3. let expiryDate: Date
  4. //...
  5. }
  6. let decoder = JSONDecoder()
  7. decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601WithFractionalSeconds)
  8. let group = try! decoder.decode(Group.self, from: data)
  9. </details>

huangapple
  • 本文由 发表于 2023年2月7日 04:06:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75366055.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定