【问题标题】:Decoding JSON into Codable Objects - with condition将 JSON 解码为可编码对象 - 有条件
【发布时间】:2019-06-19 13:17:00
【问题描述】:

我想使用 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",
            }
         ]
      }
  ]

如您所见,Collection 将是 item 或 location 类型。数据将根据该类型。 我应该如何使用 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)合适吗?我相信它以一种灵活、方便的方式解决了问题。

标签: ios json swift nsobject codable


【解决方案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

Pro:使数据更具自我描述性

Con:允许既不包含 items 也不包含 locations 的集合

方法2

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

Con:允许data 数组部分为Location,部分为Item

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 很遗憾,这对我们不起作用。 Item 和 Location 有很多属性。我必须把它们分开,否则,这将是一个巨大的沙拉。
    猜你喜欢
    • 2018-11-30
    • 2023-04-02
    • 2022-01-07
    • 2012-09-20
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多