从 JSON 访问嵌套对象,当它是表示为字符串的动态数字时

Accessing nested object from JSON when it is a dynamic number represented as string

我正在使用 XCode(10.2.1) 和 Swift(5.0) 和 运行 从 API 访问数据,遇到一个我无法解决的问题找到答案。我能够从 API 的所有其他部分获取数据,除了一个被命名为数字字符串“750”的部分,我不确定当我不能使用变量时如何获取该数据jsonDecoder可以读取吗?

这是我知道行不通的示例,但可以让您了解我正在尝试做什么。

class Images: Codable {
    let "750": String

    init("750": String){
        self."750" = "750"
    }
}

这是 API 的片段,我正在尝试从以下位置获取图像:

  "id": "069f7f26",
      "sku": "AP",
      "title": "Pizza",
      "description": "A really great pizza",
      "list_price": "9.95",
      "is_vatable": true,
      "is_for_sale": false,
      "age_restricted": false,
      "box_limit": 2,
      "always_on_menu": false,
      "volume": null,
      "zone": null,
      "created_at": "2017-03-06T10:52:43+00:00",
      "attributes": [
        {
          "id": "670f0e7c",
          "title": "Allergen",
          "unit": null,
          "value": "Products manufactured in a nut environment"
        },
        {
          "id": "c29e7",
          "title": "Weight",
          "unit": "g",
          "value": "300"
        }
      ],
      "tags": [

      ],
      "images": {
        "750": {
          "src": "https:\/\/some_website.co.uk\/cms\/product_image\some_image.jpg",
          "url": "https:\/\/some_website.co.uk\/cms\/product_image\some_image.jpg",
          "width": 750
        }
      }
    },

我设置了一个更符合您情况的示例,以便让您了解如何使用 Dictionary 数据类型动态地 parse 和访问您的 JSON 信息:

import Foundation

let jsonData = """
{
    "images": {
        "750": {
          "src": "https:\/\/some_website.co.uk/cms/product_image/some_image.jpg",
          "url": "https:\/\/some_website.co.uk/cms/product_image/some_image.jpg",
          "width": 750
        }
    }
}
"""

let json = jsonData.data(using: .utf8)!

public struct Results: Codable {
    public var images: [String:Image] = [:]

    enum CodingKeys: String, CodingKey {
        case images = "images"
    }
}

public struct Image: Codable {
    public var src: String = ""
    public var url: String = ""
    public var width: Int = 0

    enum CodingKeys: String, CodingKey {
        case src = "src"
        case url = "url"
        case width = "width"
    }
}

if let results = try? JSONDecoder().decode(Results.self, from: json) {
    let imagesDict = results.images
    for (key, value) in imagesDict {
        print("Key: \(key)")
        print("Value: \(value)")
    }
}

如果您尝试此代码段,它将为您打印以下输出:

Key: 750
Value: Image(src: "https://some_website.co.uk/cms/product_image/some_image.jpg", url: "https://some_website.co.uk/cms/product_image/some_image.jpg", width: 750)

您可以在线试用上面的代码片段,如果您将其复制粘贴到此处并 运行 它:http://online.swiftplayground.run/

### 更新(回应评论)

针对您的评论,我发现设置另一个示例更容易,以向您展示如何使用您在评论本身中共享的确切代码示例来实现这一目标。

我将所有内容都保留为 class,并添加了 images,以便让您大致了解如何实现该目标。

最后,我建议将ProductsAttributes重命名为ProductAttribute。此外,如果没有充分的理由说明为什么选择 modelclass,只需将它们更改为 struct 并且没有充分的理由保留每个模型 optional 都给它们一个默认值,就像我在上面的例子中所做的那样,如果你总是期待一些 values/attributes 在那里。

您可以尝试 运行 这个片段以及 http://online.swiftplayground.run 中的尝试:

import Foundation

let jsonData = """
{
    "data": [
        {
            "title": "titlex",
            "description": "descx",
            "list_price": "123,456",
            "attributes": [
                {
                    "title": "titlex",
                    "unit": "unitx",
                    "value": "valuex"
                }
            ],
            "images": {
                "750": {
                "src": "https:\/\/some_website.co.uk/cms/product_image/some_image.jpg",
                "url": "https:\/\/some_website.co.uk/cms/product_image/some_image.jpg",
                "width": 750
                }
            }
        }
    ]
}
"""

let json = jsonData.data(using: .utf8)!

class AllProducts: Codable {
    let data: [Products]

    init(data: [Products]) {
        self.data = data
    }
}

class Products: Codable {
    let title: String?
    let description: String?
    let list_price: String?
    let attributes: [Attributes]?
    let images: [String:Image]?

    init(title: String, description: String, list_price: String, attributes: [Attributes], images: [String:Image]) {
        self.title = title
        self.description = description
        self.list_price = list_price
        self.attributes = attributes
        self.images = images
    }
}

class Attributes: Codable {
    let title: String?
    let unit: String?
    let value: String?

    init(title: String, unit: String, value: String) {
        self.title = title
        self.unit = unit
        self.value = value
    }
}

class Image: Codable {
    let src: String?
    let url: String?
    let width: Int?

    init(src: String, url: String, width: Int) {
        self.src = src
        self.url = url
        self.width = width
    }
}

// parsing/decoding
if let results = try? JSONDecoder().decode(AllProducts.self, from: json) {
    if let imagesDict = results.data[0].images {

    // there is an "images" for product at position 0 (the only one in my json example)
        for (key, value) in imagesDict {
            print("Key: \(key)")
            print("Value src: \(value.src)")
            print("Value url: \(value.url)")
            print("Value width: \(value.width)")
        }
    }
}

输出

Key: 750
Value src: Optional("https://some_website.co.uk/cms/product_image/some_image.jpg")
Value url: Optional("https://some_website.co.uk/cms/product_image/some_image.jpg")
Value width: Optional(750)