首页 > 解决方案 > 复杂的 JSON 到 Swift

问题描述

所以我对 JSON 比较陌生,并将其转换为 Swift。我只处理了简单数据类型的基本 JSON。没有什么花哨。我也只处理过使用我创建的 JSON。

现在我正在尝试使用从 API 中收集的 JSON。此 JSON 在其他对象中嵌套了对象。

这是 JSON 的格式:

{
"records": [
    {
        "id": "Info",
        "fields": {
            "Card Name": "Info",
            "Qty": 0,
            "Card Type": "Info",
            "Color": "Info",
            "Card #": "Info",
            "Rarity": "Info",
            "Image": [
                {
                    "id": "Info",
                    "url": "url",
                    "filename": "info",
                    "size": 0,
                    "type": "info",
                    "thumbnails": {
                        "small": {
                            "url": "url",
                            "width": 0,
                            "height": 0
                        },
                        "large": {
                            "url": "url",
                            "width": 0,
                            "height": 0
                        },
                        "full": {
                            "url": "url",
                            "width": 0,
                            "height": 0
                        }
                    }
                }
            ],
            "Level": 0,
            "Full Set": 0
        },
        "createdTime": "info"
    }
],
"offset": "info"
}

我不确定我是否需要对“记录”:部分做任何事情。现在“记录”中有更多记录:数组。

我不需要每条记录中的所有信息。

我尝试做类似的事情:

struct Card {
    let offSet: String
    let cardName: String
}

extension Card: Decodable {
    enum CodingKeys: String, CodingKey {
        case offSet
        case cardName
        
        enum FieldKeys: String, CodingKey {
                case cardName = "Card Name"
            }
    }
    
    
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        offSet = try container.decode(String.self, forKey: .offSet)
        
        let fieldContainer = try container.nestedContainer(keyedBy: CodingKeys.FieldKeys.self, forKey: .cardName)
        cardName = try fieldContainer.decode(String.self, forKey: .cardName)
    }
    
}

每次我尝试将信息解码到我的 Swift 结构中时,它都会出现为零。不知道我做错了什么或从这里去哪里。我还没有处理过具有“记录”的 JSON:这样的设置。不确定这是否重要。

还有一个关于图像的附带问题。我会只使用 url 属性来显示图像吗?图像文件将是 PNG。以前从未处理过图像和 JSON。

标签: jsonswift

解决方案


正如 Larme 评论的“你的代码无法猜测” ——这意味着你需要在代码中表示与 JSON 提供的相同结构。

冒着为您做这项工作的危险,我编写了一组非常冗长的Codable struct's,它们从您提供的 JSON 中提取所有数据。

struct Card: Codable {
    var records:[Record]
    var offset:String
}

struct Record: Codable {
    var id:String
    var fields:Fields
    var createdTime:String
}

struct Fields: Codable {
    var name:String
    var quantity:Int
    var type:String
    var color:String
    var number:String
    var rarity:String
    var level:Int
    var fullSet:Int
    var image:[Image]
    enum CodingKeys: String, CodingKey {
        case name = "Card Name"
        case quantity = "Qty"
        case type = "Card Type"
        case color = "Color"
        case number = "Card #"
        case rarity = "Rarity"
        case level = "Level"
        case fullSet = "Full Set"
        case image = "Image"
    }
}

struct Image: Codable {
    var id:String
    var url:String
    var filename:String
    var size:Int
    var type:String
    var thumbnails:Thumbnails
}

struct Thumbnails: Codable {
    var small:Thumbnail
    var large:Thumbnail
    var full:Thumbnail
}

struct Thumbnail: Codable {
    var url:String
    var width:Int
    var height:Int
}

我通过将您提供的 JSON 粘贴为多行字符串并使用以下内容转储生成的对象,在操场上测试了上述内容。

do {
    if let data = json.data(using: .utf8) {
        let card = try JSONDecoder().decode(Card.self, from: data)
        dump(card)
    }
} catch {
    fatalError("Failed to decode \(error)")
}

其输出是:

▿ __lldb_expr_21.Card
  ▿ records: 1 element
    ▿ __lldb_expr_21.Record
      - id: "Info"
      ▿ fields: __lldb_expr_21.Fields
        - name: "Info"
        - quantity: 0
        - type: "Info"
        - color: "Info"
        - number: "Info"
        - rarity: "Info"
        - level: 0
        - fullSet: 0
        ▿ image: 1 element
          ▿ __lldb_expr_21.Image
            - id: "Info"
            - url: "url"
            - filename: "info"
            - size: 0
            - type: "info"
            ▿ thumbnails: __lldb_expr_21.Thumbnails
              ▿ small: __lldb_expr_21.Thumbnail
                - url: "url"
                - width: 0
                - height: 0
              ▿ large: __lldb_expr_21.Thumbnail
                - url: "url"
                - width: 0
                - height: 0
              ▿ full: __lldb_expr_21.Thumbnail
                - url: "url"
                - width: 0
                - height: 0
      - createdTime: "info"
  - offset: "info"

一旦你有了所有的数据,你就可以开始简化事情了,也许是对 Card 的扩展,以获得第一个记录名称。

extension Card {
    var cardName:String {
        guard let firstRecord = self.records.first else {
            return "Unknown Name"
        }
        return firstRecord.fields.name
    }
}

print(card.cardName) // "Info\n"

推荐阅读