将 JSON 解码为可编码对象 - 有条件

Decoding JSON into Codable Objects - with condition

我想将 JSON 解码为具有 Codable 协议的对象。

我想要达到的结果是:

[
 [ Collection
    < collectionType = item
    < collectionName = some name`
    < data = [ Item
                 < itemTitle = title
                 < itemSubtitle = subtitle,
               Item
                 < itemTitle = title
                 < itemSubtitle = subtitle ],
[ Collection
    < collectionType = location
    < collectionName = some name`
    < data = [ Location
                 < locationName = someName,
               Location
                 < locationName = someName ],
[ Collection
    < collectionType = item
    < collectionName = some name`
    < data = [ Item
                 < itemTitle = title
                 < itemSubtitle = subtitle,
               Item
                 < itemTitle = title
                 < itemSubtitle = subtitle ],
[ Collection
    < collectionType = location
    < collectionName = some name`
    < data = [ Location
                 < locationName = someName,
               Location
                 < locationName = someName ]]

JSON如下:

    [{
        "collectionType": "item",
        "collectionName": "some name",
        "data": [
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            },
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            }
         ]
      },
      {
        "collectionType": "location",
        "collectionName": "some name",
        "data": [
            {
                "locationName": "a name",
            },
            {
                "locationName": "a name",
            }
         ]
      },
      {
        "collectionType": "item",
        "collectionName": "some name",
        "data": [
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            },
            {
                "itemTitle": "title",
                "itemSubtitle": "subtitle",
            }
         ]
      },
      {
        "collectionType": "location",
        "collectionName": "some name",
        "data": [
            {
                "locationName": "a name",
            },
            {
                "locationName": "a name",
            }
         ]
      }
  ]

如您所见,集合将属于项目或位置类型。数据将根据该类型。 我应该如何使用 Codable 实现这一目标?

我的对象如下:

class Collection: NSObject, Codable {

    // MARK: - Properties

    let collectionType: String
    let collectionName: String
    let data????

    // MARK: - Keyes

    private enum CodingKeys: String, CodingKey {
        case collectionType
        case collectionName
    }
}

class Item: NSObject, Codable {

    // MARK: - Properties

    let itemTitle: String
    let itemSubtitle: String

    // MARK: - Keyes

    private enum CodingKeys: String, CodingKey {
        case itemTitle
        case itemSubtitle
    }
}

class Location: NSObject, Codable {

    // MARK: - Properties

    let locationName: String

    // MARK: - Keyes

    private enum CodingKeys: String, CodingKey {
        case locationName
    }
}

如何使用适当的对象传播数据?

我建议您可以使用具有多个属性和可选值的公共 class,而不是使用条件解析,并根据需要使用它。请参考以下代码。

例如如果itemTitlenil那么执行locationName的逻辑等等。

class Collection: NSObject, Codable {
    let collectionType: String
    let collectionName: String
    let data:data?

    private enum CodingKeys: String, CodingKey {
        case collectionType
        case collectionName
    }
}

class data: NSObject, Codable {

    let itemTitle: String?
    let itemSubtitle: String?
    let locationName: String?

    private enum CodingKeys: String, CodingKey {
        case itemTitle
        case itemSubtitle
        case locationName
    }
}

我建议两种方法:

方法一

更改您的数据结构以消除 data 描述项目或位置的歧义:

[{
    "collectionName": "some name",
    "items": [
        {
            "itemTitle": "title",
            "itemSubtitle": "subtitle",
        },
        {
            "itemTitle": "title",
            "itemSubtitle": "subtitle",
        }
    ]
},
{
    "collectionName": "some name",
    "locations": [
        {
            "locationName": "a name",
        },
        {
            "locationName": "another name",
        }
    ]
}]

... 并修改您的 Collection 以具有可选的 locations 和可选的 items.

方法二

如果无法更改 JSON 结构,那么我建议将 Collection class 更改为:

class Collection: Codable {
    let collectionType: String
    let collectionName: String
    let data: [CollectionData]
}

... 并创建枚举 CollectionData:

enum CollectionError: Error {
    case invalidData
}

enum CollectionData {
    case item(Item)
    case location(Location)
}

extension CollectionData: Codable {
    init(from decoder: Decoder) throws {
        if let item = try? Item(from: decoder) {
            self = .item(item)
            return
        }

        if let location = try? Location(from: decoder) {
            self = .location(location)
            return
        }

        throw CollectionError.invalidData
    }

    func encode(to encoder: Encoder) throws {
        switch self {
        case .item(let item):
            try item.encode(to: encoder)
        case .location(let location):
            try location.encode(to: encoder)
        }
    }
}

两种方法的优缺点:

方法一

专业版:让数据更多self-descriptive

缺点:允许集合既没有 items 也没有 locations

方法二

专业版:适用于现有数据结构

缺点:将允许 data 数组部分为 Location 部分为 Item

除非您的实际代码有更多内容,否则您似乎将 CodingKeys 定义为与默认值完全相同,因此您可以删除它。