【问题标题】:Swift JSON decoder different typesSwift JSON解码器不同类型
【发布时间】:2019-12-23 20:33:14
【问题描述】:

我有这两个 JSON 对象

[ 
{"name": "Popular Movies", 
"description": "Basic movie description", 
"type": "movies", 
"items": [ { "id": 15, "name": "Sample movie", "movieSPT": ""}]
},
{"name": "Popular TV Shows", 
"description": "Basic shows description", 
"type": "tvshows", 
"items": [ { "id": 15, "name": "Sample show", "showSPT": ""}]
}
]

然后我为节目和电影创建了两个可解码结构:

struct Movie: Decodable {
    let id: Int
    let name: String
    let movieSPT: String
}

struct TVShow: Decodable {
    let id: Int
    let name: String
    let showSPT: String
}

那么,当我为主要响应创建对象时,创建项目数组的最佳方法是什么,取决于类型值?我知道我可以为某些独特的结构创建带有可选属性的 showSPT 和 movieSPT,但这是正确的方法吗?另外,如果这两个模型有很多属性,combine struct 就会太大。

struct Blocks : Decodable {
    let name: String
    let description: String
    let type: String
    let items: [Movie] or [Show] based on type????
}

【问题讨论】:

  • AlamoFire 是一个非常好用且简单的 json 解码库,可以通过 cocoapods 安装
  • 抱歉,这里的目标是什么?忘记 JSON,想想你想要什么。就目前而言,任何数组都不能同时包含电影和电视节目。那么你的目标是什么?一种结构类型和一种数组?两个结构类型和两个数组?什么?
  • 另请注意,您的结构与现有的 JSON 完全不同。 讲了两种类型,但是JSON只有一种。为什么不直接解码成与 JSON 匹配的内容?
  • @matt 没有数组可以同时包含电影和电视节目。其实可以。一个通用协议(在Codable 上下文中不可用)和一个具有关联类型的枚举怎么样
  • @vadian 我说的是“按现状”,意思是定义的 OP 电影和电视节目。 “通用协议”当然是一种解决方案,但 OP 没有。这就是我的观点。正如我在下一条评论中建议的那样,需要更清楚目标是什么。

标签: json swift parsing


【解决方案1】:

有几个解决方案。其中之一是具有关联类型的枚举

let jsonString = """
[{"name": "Popular Movies", "description": "Basic movie description", "type": "movies",
    "items": [ { "id": 15, "name": "Sample movie", "movieSPT": ""}]
},
{"name": "Popular TV Shows", "description": "Basic shows description", "type": "tvshows",
"items": [ { "id": 15, "name": "Sample show", "showSPT": ""}]
}
]
"""
let data = Data(jsonString.utf8)


struct Movie : Decodable {
    let id: Int
    let name, movieSPT: String
}

struct TVShow : Decodable {
    let id: Int
    let name, showSPT: String
}

enum MediaType {
    case movie([Movie]), tvShow([TVShow])
}

struct Media : Decodable {
    let name : String
    let description : String
    let items : MediaType

    private enum CodingKeys : String, CodingKey { case name, description, type, items }

    init(from decoder : Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        self.description = try container.decode(String.self, forKey: .description)
        let type = try container.decode(String.self, forKey: .type)
        if type == "movies" {
            let movieData = try container.decode([Movie].self, forKey: .items)
            items = .movie(movieData)
        } else { // add better error handling
            let showData = try container.decode([TVShow].self, forKey: .items)
            items = .tvShow(showData)
        }

    }
}

do {
    let result = try JSONDecoder().decode([Media].self, from: data)
    print(result)
} catch {
    print(error)
}

【讨论】:

    【解决方案2】:

    您可以对 JSON 进行解码,然后按类型过滤出解码后的数组。

        import Foundation
    
        let jsonData = """
        [
        {"name": "Popular Movies",
        "description": "Basic movie description",
        "type": "movies",
        "items": [ { "id": 15, "name": "Sample movie", "movieSPT": ""}]
        },
        {"name": "Popular TV Shows",
        "description": "Basic shows description",
        "type": "tvshows",
        "items": [ { "id": 15, "name": "Sample show", "showSPT": ""}]
        }
        ]
        """.data(using: .utf8)!
    
    
        // MARK: - MediaElement
    struct MediaElement: Codable {
        let name, mediaDescription, type: String
        let items: [Item]
    
        enum CodingKeys: String, CodingKey {
            case name
            case mediaDescription = "description"
            case type, items
        }
    }
    
    // MARK: - Item
    struct Item: Codable {
        let id: Int
        let name: String
        let movieSPT, showSPT: String?
    }
    
    typealias Media = [MediaElement]
    
    
    let decoder = JSONDecoder()
    
    let media = try? decoder.decode(Media.self, from: jsonData)
    
    let moviesMediaArray = media?.filter {$0.type == "movies"}
    let tvshowsMediaArray = media?.filter {$0.type == "tvshows"}
    

    【讨论】:

      猜你喜欢
      • 2019-07-28
      • 2018-06-09
      • 1970-01-01
      • 2021-02-22
      • 1970-01-01
      • 2020-04-09
      • 2023-03-11
      相关资源
      最近更新 更多