从Swift Decodables访问枚举的合适方法是什么?

时间:2018-09-16 01:42:32

标签: ios json swift api quicktype

我有一个非常奇怪的情况,我使用JSON并认为我可以一直将子字符串化为要访问的数据,但是,它无法按预期工作。

使用QuickType,我可以转换以下JSON:https://kidsuper.fodalabs.com/wp-json/wp/v2/art

到下面。

尝试访问时,似乎应该可以执行.acf.gallery.id,但是一旦我进入acf.gallery,它就会说.id不存在。这很奇怪,但是当我尝试

时会返回以下内容
let temp = imageArray[indexPath.row].acf.gallery.id
Value of type 'GalleryUnion?' has no member 'id'

只是为了好玩,我尝试了这个,也没有运气:

let temp = imageArray[indexPath.row].acf.GalleryUnion.galleryElementArray
    Error

:类型为“ Acf”的值没有成员“ GalleryUnion”

我打印.acf.gallery时的返回是这样的:

Acf(company: "Season #3", 
     gallery: Optional(weddingszn.GalleryUnion.galleryElementArray([weddingszn.GalleryElement(
         id: 135, galleryID: 135, title: "1-791x1024", 
         filename: "1-791x1024.jpg", url: "https://kidsuper.fodalabs.com/wp-content/up

下面是我要解析的完整代码。有任何想法吗?

struct Acf: Codable {
    let company: String
    let gallery: GalleryUnion?
    let tagline: String
    let featuredImg: Bool?

    enum CodingKeys: String, CodingKey {
        case company, gallery, tagline
        case featuredImg = "featured_img"
    }
}

enum GalleryUnion: Codable {
    case bool(Bool)
    case galleryElementArray([GalleryElement])

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Bool.self) {
            self = .bool(x)
            return
        }
        if let x = try? container.decode([GalleryElement].self) {
            self = .galleryElementArray(x)
            return
        }
        throw DecodingError.typeMismatch(GalleryUnion.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for GalleryUnion"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .bool(let x):
            try container.encode(x)
        case .galleryElementArray(let x):
            try container.encode(x)
        }
    }
}

struct GalleryElement: Codable {
    let id, galleryID: Int
    let title, filename: String
    let url: String
    let alt, author, description, caption: String
    let name, date, modified: String
    let mimeType: MIMEType
    let type: TypeEnum
    let icon: String
    let width, height: Int
    let sizes: Sizes

    enum CodingKeys: String, CodingKey {
        case id = "ID"
        case galleryID = "id"
        case title, filename, url, alt, author, description, caption, name, date, modified
        case mimeType = "mime_type"
        case type, icon, width, height, sizes
    }
}

3 个答案:

答案 0 :(得分:2)

在深入探究数组之前,必须先使用if case,guard case或switch case来打开枚举的包装。

if case .galleryElementArray(let x) = imageArray[indexPath.row].acf.gallery {
    print(x.first!.id)
}

答案 1 :(得分:2)

  

但是,一旦我进入acf.gallery,它就会说.id不存在。这很奇怪

不,不是。根据您的代码,.gallery应该是GalleryUnion。好吧,GalleryUnion没有.id。它根本没有任何属性。 GalleryElement具有.id,但是GalleryUnion不是GalleryElement。因此,我看不出您的惊喜来自何处;这里没有惊喜。

GalleryUnion有个案例。您需要检查这是哪种情况。如果为. galleryElementArray,则需要提取关联的值。即使那样,您也不会有任何.id,因为您现在拥有的仍然不是GalleryElement;这是GalleryElements的 array

您可以通过定义带有额外计算属性的GalleryUnion来简化此工作,该属性为您获取关联的值:

enum GalleryUnion : Codable {
    case bool(Bool)
    case galleryElementArray([GalleryElement])
    var galleryElements : [GalleryElement]? {
        switch self {
        case .bool : return nil
        case let .galleryElementArray(arr): return arr
        }
    }
    // ... and so on
}

至少,您可以这样说:

act.gallery.galleryElements?.map {$0.id}

...或者您有什么想法。

答案 2 :(得分:1)

因此,GalleryUnion可以做两件事之一。它既可以.bool(_)也可以galleryElementArray(_)。当您要访问实际的基础值时,需要确定其处于哪个状态。

要在Swift中执行此操作,可以使用switch语句。然后,您可以使用它来访问内部包含的值。也许类似于:

if let gallery = acf.gallery {
    switch gallery {
    case .galleryElementArray(let values):
        values.forEach {
            print($0.id)
        }
    case .bool(_):
        break
    }
}

您可能希望阅读Enumerations,请查找“关联值”部分