基于另一个值的具有多种对象类型的可编码解码属性

Posted

技术标签:

【中文标题】基于另一个值的具有多种对象类型的可编码解码属性【英文标题】:Codable decode property with multiple object types BASED on another value 【发布时间】:2019-09-04 09:37:57 【问题描述】:

这与解码具有多种类型(int、string)的属性值无关..

我有一个名为 data 的对象,它可以返回多种类型,此时可以执行的操作可能如下所示:

enum MyData: Codable 
    case ObjOne(groupObject)
    case ObjTwo(imageObject)

    init(from decoder: Decoder) throws 
        let value = try decoder.singleValueContainer()

        if let v = try? value.decode(groupObject.self) 
            self = .ObjOne(v)
            return
         else if let v = try? value.decode(imageObject.self) 
            self = .ObjTwo(v)
            return
        

        throw Rating.ParseError.notRecognizedType(value)
    

    enum ParseError: Error 
        case notRecognizedType(Any)
    

这里的问题是我试图让MyData根据之前解码过程中使用的另一个值来解码对象,简而言之,我想将一个值传递给MyData,以便它可以确定哪个解码

我有这个

enum ContentType: String, Codable 
    case linear
    case grid
    case slider

我想让MyData 知道这个ContentType 值,以便MyData 可以确定流程将如何进行,

那么 ContentType 是从哪里来的呢?它在前一个主对象的相同属性列表中,来自看起来像这样的东西

struct Catalog: Codable 
    var dataType: ContentType?
    var data: MyData? 

我想用更简单的话实现什么?

struct Catalog: Codable 
    var dataType: ContentType?
    var data: MyData<dataType>? <--// i know this is not possible,
    // -- but i want MyData to know about the dataType value that will be decoded

--------- 我要解析的 JSON

[
  "data_type": "group",
  "data": 
    "group_id": 127 // this refers to object : groupObject
  
,

  "data_type": "image",
  "data": 
    "image": "http://google.com/favicon" // this is referring : imageObject
  

]

你看上面的点,就是“data”可以根据data_type的值返回不同的个对象

【问题讨论】:

您应该在问题中包含一些示例 JSON。 @DávidPásztor 你是对的,这让它变得更好了,我已经包含了一个 JSON。 【参考方案1】:

我没有使用泛型,而是创建了一个符合Decodable 的空协议并将其用作data 的类型。那么内容结构体需要符合这个协议。

protocol MyData: Decodable 

struct Group: MyData 
    let groupId: Int


struct Image: MyData 
    let image: String


struct Catalog: Decodable 
    var dataType: String
    var data: MyData

    enum CodingKeys: String, CodingKey  
        case dataType, data
    

    enum ParseError: Error 
        case notRecognizedType(Any)
    

    init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)
        dataType = try container.decode(String.self, forKey: .dataType)
        switch dataType 
        case "group":
            data = try container.decode(Group.self, forKey: .data)
        case "image":
            data = try container.decode(Image.self, forKey: .data)
        default:
            throw ParseError.notRecognizedType(dataType)
        
    

请注意,我没有在 init 中使用枚举 ContentType,因为它与示例 json 数据不匹配,但这应该很容易修复。

使用此解决方案的标准代码

do 
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase

    let result = try decoder.decode([Catalog].self, from: data)
    print(result)
 catch 
    print(error)

【讨论】:

以上是关于基于另一个值的具有多种对象类型的可编码解码属性的主要内容,如果未能解决你的问题,请参考以下文章

Swift 4 可编码;如何使用单个根级密钥解码对象

Alamofire:具有额外属性的可编码对象

如何为可编码对象提供自定义代码功能

使用“可编码”解码数据

Python数据对象的编码和解码,json和pickle模块,base64模块的简单使用

可编码为 CKRecord