【问题标题】:Decode heterogeneous array JSON using Swift decodable使用 Swift decodedable 解码异构数组 JSON
【发布时间】:2020-06-25 19:36:54
【问题描述】:

这是我要解码的 JSON。 objectType 的值决定了要创建什么对象。

{
  "options": [
    {
      "objectType": "OptionTypeA",
      "label": "optionALabel1",
      "value": "optionAValue1"
    },
    {
      "objectType": "OptionTypeB",
      "label": "optionBLabel",
      "value": "optionBValue"
    },
    {
      "objectType": "OptionTypeA",
      "label": "optionALabel2",
      "value": "optionAValue2"
    }
  ]
}

假设我有这样定义的 2 个选项类型

public protocol OptionType {
  var label: String { get }
  var value: String { get }
}

struct OptionTypeA: Decodable {
  let label: String
  let value: String
  
  // and some others...
}

struct OptionTypeB: Decodable {
  let label: String
  let value: String
  
  // and some others...
}

struct Option: Decodable {
  let options: [OptionType]
  
  enum CodingKeys: String, CodingKey {
    case options
    case label
    case value
    case objectType
  }
  
  init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    var optionsContainer = try values.nestedUnkeyedContainer(forKey: .options)
    var options = [OptionType]()
    
    while !optionsContainer.isAtEnd {
      let itemContainer = try optionsContainer.nestedContainer(keyedBy: CodingKeys.self)
      
      switch try itemContainer.decode(String.self, forKey: .objectType) {
      // What should I do here so that I do not have to manually decode `OptionTypeA` and `OptionTypeB`?
      case "OptionTypeA": options.append() 
      case "OptionTypeB": options.append()
      default: fatalError("Unknown type")
      }
    }
    
    self.options = options
  }
}

我知道我可以手动解码itemContainer 中的每个键,并在开关盒中创建单独的选项类型对象。但我不想那样做。我怎样才能解码这些对象?

【问题讨论】:

    标签: json swift decodable heterogeneous


    【解决方案1】:

    比通用属性的协议更快捷的方式是一个带有关联值的枚举。

    Option 枚举首先解码 objectType——它甚至可以解码为枚举——并根据值解码不同的结构。

    enum OptionType : String, Decodable {
        case a = "OptionTypeA", b = "OptionTypeB"
    }
    
    struct Root : Decodable {
        let options : [Option]
    }
    
    enum Option : Decodable {
        private enum CodingKeys : String, CodingKey { case objectType }
        
        case typeA(OptionTypeA)
        case typeB(OptionTypeB)
        
        init(from decoder : Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            let typeContainer = try decoder.singleValueContainer()
            let optionType = try container.decode(OptionType.self, forKey: .objectType)
            switch optionType {
                case .a: self = .typeA(try typeContainer.decode(OptionTypeA.self))
                case .b: self = .typeB(try typeContainer.decode(OptionTypeB.self))
            }
        }
    }
    
    struct OptionTypeA: Decodable {
      let label: String
      let value: String
      
      // and some others...
    }
    
    struct OptionTypeB: Decodable {
      let label: String
      let value: String
      
      // and some others...
    }
    

    let jsonString = """
    {
      "options": [
        {
          "objectType": "OptionTypeA",
          "label": "optionALabel1",
          "value": "optionAValue1"
        },
        {
          "objectType": "OptionTypeB",
          "label": "optionBLabel",
          "value": "optionBValue"
        },
        {
          "objectType": "OptionTypeA",
          "label": "optionALabel2",
          "value": "optionAValue2"
        }
      ]
    }
    """
    
    let data = Data(jsonString.utf8)
    
    do {
        let result = try JSONDecoder().decode(Root.self, from: data)
        for option in result.options {
            switch option {
                case .typeA(let optionTypeA): print(optionTypeA)
                case .typeB(let optionTypeB): print(optionTypeB)
            }
        }
    } catch {
        print(error)
    }
    

    【讨论】:

    • 谢谢!这有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 2020-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-24
    相关资源
    最近更新 更多