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

时间:2019-06-19 13:17:00

标签: ios json swift nsobject codable

我想使用可编码协议将JSON解码为对象。

我想要实现的结果是:

[
 [ 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
    }
}

如何传播带有适当对象的数据?

2 个答案:

答案 0 :(得分:1)

我建议两种方法:

方法1

更改数据结构以消除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

方法2

如果无法更改JSON结构,则建议将Collection类更改为:

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)
        }
    }
}

两种方法的优缺点:

方法1

专业版:使数据更具描述性

骗局:不允许同时使用itemslocations

进行收集

方法2

专业版:使用现有数据结构

缺点:将允许部分为data和部分Location的{​​{1}}数组

除非您的真实代码有更多内容,否则您似乎完全按照默认设置来定义Item,因此可以将其删除。

答案 1 :(得分:0)

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

例如,如果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
    }
}