Декодируемые вложенные данные без создания дополнительного класса в Swift - PullRequest
0 голосов
/ 10 ноября 2018

Я новичок в разработке для iOS, поэтому извините за глупый вопрос заранее. У меня есть JSON, как это:

{
   "type":"post",
   "comments":{
      "count":0,
      "can_post":1
   },
   "likes":{
      "count":0,
      "user_likes":0,
      "can_like":1,
      "can_publish":1
   },
   "reposts":{
      "count":0,
      "user_reposted":0
   }
}

Я хочу преобразовать это в класс, который будет содержать просто likesCount, commentsCount, repostsCount, но без создания отдельных классов для comments, likes, reposts. Я использую Decodable для этого и вот мой код, который не работает:)

Код:

final class FeedItem: Decodable {
    enum Keys: String, CodingKey {
        case type,
        likes = "likes.count",
        comments = "comments.count",
        reposts = "reposts.count"
    }

    let type: String
    var likes = 0
    var comments = 0
    var reposts = 0

    required convenience init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Keys.self)
        let type = try container.decode(String.self, forKey: .type)
        let likes = try container.decode(Int.self, forKey: .likes)
        let comments = try container.decode(Int.self, forKey: .comments)
        let reposts = try container.decode(Int.self, forKey: .reposts)
        self.init(type: type, likes: likes, comments: comments, reposts: reposts)
    }

    init(type: String,
         likes: Int,
         comments: Int,
         reposts: Int) {
        self.type = type
        self.likes = likes
        self.comments = comments
        self.reposts = reposts
    }
}

Ошибка:

"No value associated with key Keys(stringValue: \"likes.count\", intValue: nil) (\"likes.count\")."

1 Ответ

0 голосов
/ 10 ноября 2018

Ошибка очень ясна, для ассоциированных ключей нет значений likes.count, так как ключ не существует.

Использование

let container = try decoder.container(keyedBy: CodingKeys.self)

и попробуйте container.decodeIfPresent(:_), чтобы проверить, существует ли ключ или, если нет, присвоить пустое значение.

Код:

struct FeedItem: Codable {

    let type: String
    let commentsCount: Int
    let canPostComment: Int
    let likesCount: Int
    let userLikes: Int
    let canLike: Int
    let canPublish: Int
    let repostsCount: Int
    let userReposted: Int

    enum CodingKeys: String, CodingKey {
        case type = "type"
        case comments = "comments"
        case likes = "likes"
        case reposts = "reposts"
        case count = "count"
        case canPost = "can_post"
        case userLikes = "user_likes"
        case canLike = "can_like"
        case canPublish = "can_publish"
        case userReposted = "user_reposted"
    }

    init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.type = try container.decodeIfPresent(String.self, forKey: .type) ?? ""

        let comments = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .comments)
        self.commentsCount = try comments.decodeIfPresent(Int.self, forKey: .count) ?? 0
        self.canPostComment = try comments.decodeIfPresent(Int.self, forKey: .canPost) ?? 0

        let likes = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .likes)
        self.likesCount = try likes.decodeIfPresent(Int.self, forKey: .count) ?? 0
        self.userLikes = try likes.decodeIfPresent(Int.self, forKey: .userLikes) ?? 0
        self.canLike = try likes.decodeIfPresent(Int.self, forKey: .canLike) ?? 0
        self.canPublish = try likes.decodeIfPresent(Int.self, forKey: .canPublish) ?? 0

        let reposts = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .reposts)
        self.repostsCount = try reposts.decodeIfPresent(Int.self, forKey: .count) ?? 0
        self.userReposted = try reposts.decodeIfPresent(Int.self, forKey: .userReposted) ?? 0
    }

    func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(type, forKey: .type)

        var comments = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .comments)
        try comments.encode(commentsCount, forKey: .count)
        try comments.encode(canPostComment, forKey: .canPost)

        var likes = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .likes)
        try likes.encode(likesCount, forKey: .count)
        try likes.encode(userLikes, forKey: .userLikes)
        try likes.encode(canLike, forKey: .canLike)
        try likes.encode(canPublish, forKey: .canPublish)

        var reposts = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .reposts)
        try reposts.encode(repostsCount, forKey: .count)
        try reposts.encode(userReposted, forKey: .userReposted)
    }
}

Чтение данных:

let data = //Your JSON data from API
let jsonData = try JSONDecoder().decode(FeedItem.self, from: data)
print("\(jsonData.type) \(jsonData.canLike)")
...