Ios 如何使用任意可编码类型

Ios 如何使用任意可编码类型,ios,swift,codable,decodable,encodable,Ios,Swift,Codable,Decodable,Encodable,我目前在项目中使用Codable类型,面临一个问题 struct Person: Codable { var id: Any } 上述代码中的id可以是String或Int。这就是id属于Any类型的原因 我知道任何都是不可编码的 我需要知道的是如何使它工作。Codable需要知道要转换到的类型 首先,我将尝试解决不知道类型的问题,看看您是否可以修复该问题并使其更简单 否则,我目前唯一能想到的解决问题的方法就是使用下面这样的泛型 struct Person<T> {

我目前在项目中使用
Codable
类型,面临一个问题

struct Person: Codable
{
    var id: Any
}
上述代码中的
id
可以是
String
Int
。这就是
id
属于
Any
类型的原因

我知道
任何
都是不可
编码的


我需要知道的是如何使它工作。

Codable需要知道要转换到的类型

首先,我将尝试解决不知道类型的问题,看看您是否可以修复该问题并使其更简单

否则,我目前唯一能想到的解决问题的方法就是使用下面这样的泛型

struct Person<T> {
    var id: T
    var name: String
}

let person1 = Person<Int>(id: 1, name: "John")
let person2 = Person<String>(id: "two", name: "Steve")
struct-Person{
变量id:T
变量名称:String
}
let person1=人(id:1,姓名:“John”)
let person2=人(id:“2”,姓名:“Steve”)

您可以用接受
Int
字符串的枚举替换
任何

enum Id: Codable {
    case numeric(value: Int)
    case named(name: String)
}

struct Person: Codable
{
    var id: Id
}

然后编译器会抱怨
Id
不符合
Decodable
。因为
Id
有相关的值,所以您需要自己实现它。请阅读如何执行此操作的示例。

我解决了这个问题,定义了一个名为AnyDecodable的新可解码结构,因此我使用AnyDecodable来代替AnyDecodable。它也可以完美地处理嵌套类型

在操场上试试这个:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)
let stud = try! JSONDecoder().decode(AnyCodable.self, from: jsonData)
print(stud.value as! [String: Any])

let backToJson = try! JSONEncoder().encode(stud)
let jsonString = String(bytes: backToJson, encoding: .utf8)!

print(jsonString)
如果您对编码部分也感兴趣,可以将我的结构扩展为任意可编码

编辑:我真的做到了

这是任何可以编辑的

struct AnyCodable: Decodable {
  var value: Any

  struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  init(value: Any) {
    self.value = value
  }

  init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyCodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyCodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

extension AnyCodable: Encodable {
  func encode(to encoder: Encoder) throws {
    if let array = value as? [Any] {
      var container = encoder.unkeyedContainer()
      for value in array {
        let decodable = AnyCodable(value: value)
        try container.encode(decodable)
      }
    } else if let dictionary = value as? [String: Any] {
      var container = encoder.container(keyedBy: CodingKeys.self)
      for (key, value) in dictionary {
        let codingKey = CodingKeys(stringValue: key)!
        let decodable = AnyCodable(value: value)
        try container.encode(decodable, forKey: codingKey)
      }
    } else {
      var container = encoder.singleValueContainer()
      if let intVal = value as? Int {
        try container.encode(intVal)
      } else if let doubleVal = value as? Double {
        try container.encode(doubleVal)
      } else if let boolVal = value as? Bool {
        try container.encode(boolVal)
      } else if let stringVal = value as? String {
        try container.encode(stringVal)
      } else {
        throw EncodingError.invalidValue(value, EncodingError.Context.init(codingPath: [], debugDescription: "The value is not encodable"))
      }

    }
  }
}
您可以在操场上以这种方式使用前面的json进行测试:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)
let stud = try! JSONDecoder().decode(AnyCodable.self, from: jsonData)
print(stud.value as! [String: Any])

let backToJson = try! JSONEncoder().encode(stud)
let jsonString = String(bytes: backToJson, encoding: .utf8)!

print(jsonString)

首先,正如您在其他答案和评论中看到的,使用
Any
,因为这不是一个好的设计。如果可能的话,再考虑一下

也就是说,如果出于自己的原因而坚持使用它,那么应该编写自己的编码/解码,并在序列化的JSON中采用某种约定

下面的代码通过将
id
始终编码为字符串,并根据找到的值解码为
Int
string

import Foundation

struct Person: Codable {
    var id: Any

    init(id: Any) {
        self.id = id
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Keys.self)
        if let idstr = try container.decodeIfPresent(String.self, forKey: .id) {
            if let idnum = Int(idstr) {
                id = idnum
            }
            else {
                id = idstr
            }
            return
        }
        fatalError()
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: Keys.self)
        try container.encode(String(describing: id), forKey: .id)
    }

    enum Keys: String, CodingKey {
        case id
    }
}

extension Person: CustomStringConvertible {
    var description: String { return "<Person id:\(id)>" }
}
用字符串
id
编码对象:

var p1 = Person(id: 1)
print(String(data: try JSONEncoder().encode(p1), 
      encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"1"}
var p2 = Person(id: "root")
print(String(data: try JSONEncoder().encode(p2), 
      encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"root"}
print(try JSONDecoder().decode(Person.self, 
      from: "{\"id\": \"2\"}".data(using: String.Encoding.utf8)!))
// <Person id:2>
print(try JSONDecoder().decode(Person.self, 
      from: "{\"id\": \"admin\"}".data(using: String.Encoding.utf8)!))
// <Person id:admin>
解码为数字
id

var p1 = Person(id: 1)
print(String(data: try JSONEncoder().encode(p1), 
      encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"1"}
var p2 = Person(id: "root")
print(String(data: try JSONEncoder().encode(p2), 
      encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"root"}
print(try JSONDecoder().decode(Person.self, 
      from: "{\"id\": \"2\"}".data(using: String.Encoding.utf8)!))
// <Person id:2>
print(try JSONDecoder().decode(Person.self, 
      from: "{\"id\": \"admin\"}".data(using: String.Encoding.utf8)!))
// <Person id:admin>
另一种实现方式是编码为
Int
String
,并将解码尝试包装在
do…catch

在编码部分:

    if let idstr = id as? String {
        try container.encode(idstr, forKey: .id)
    }
    else if let idnum = id as? Int {
        try container.encode(idnum, forKey: .id)
    }
然后多次尝试解码到正确的类型:

do {
    if let idstr = try container.decodeIfPresent(String.self, forKey: .id) {
        id = idstr
        id_decoded = true
    }
}
catch {
    /* pass */
}

if !id_decoded {
    do {
        if let idnum = try container.decodeIfPresent(Int.self, forKey: .id) {
            id = idnum
        }
    }
    catch {
        /* pass */
    }
}
在我看来,这更难看

根据您对服务器序列化的控制,您可以使用它们中的任何一个,也可以编写适合实际序列化的其他内容。

Quantum Value 首先,您可以定义一个类型,该类型可以从
字符串和
Int
值进行解码。 给你

enum QuantumValue: Decodable {
    
    case int(Int), string(String)
    
    init(from decoder: Decoder) throws {
        if let int = try? decoder.singleValueContainer().decode(Int.self) {
            self = .int(int)
            return
        }
        
        if let string = try? decoder.singleValueContainer().decode(String.self) {
            self = .string(string)
            return
        }
        
        throw QuantumError.missingValue
    }
    
    enum QuantumError:Error {
        case missingValue
    }
}
人 现在可以这样定义结构

struct Person: Decodable {
    let id: QuantumValue
}
就这样。让我们测试一下

JSON 1:
id
String
JSON 2:
id
is
Int
更新1比较值 这一新段落应回答评论中的问题

如果要将量子值与
Int
进行比较,必须记住量子值可以包含
Int
字符串

所以问题是:比较
字符串和
Int
意味着什么

如果您只是在寻找一种将量子值转换为
Int
的方法,那么只需添加此扩展即可

extension QuantumValue {
    
    var intValue: Int? {
        switch self {
        case .int(let value): return value
        case .string(let value): return Int(value)
        }
    }
}
现在你可以写作了

let quantumValue: QuantumValue: ...
quantumValue.intValue == 123
更新2 本部分将回答@Abrcd18留下的评论

您可以将此计算属性添加到
Person
结构中

var idAsString: String {
    switch id {
    case .string(let string): return string
    case .int(let int): return String(int)
    }
}
现在要填充标签,只需写

label.text = person.idAsString

希望有帮助。

如果您的问题是无法确定id的类型,因为它可能是字符串或整数值,我可以建议您发表以下博文:

基本上,我定义了一个新的可解码类型

public struct UncertainValue<T: Decodable, U: Decodable>: Decodable {
    public var tValue: T?
    public var uValue: U?

    public var value: Any? {
        return tValue ?? uValue
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        tValue = try? container.decode(T.self)
        uValue = try? container.decode(U.self)
        if tValue == nil && uValue == nil {
            //Type mismatch
            throw DecodingError.typeMismatch(type(of: self), DecodingError.Context(codingPath: [], debugDescription: "The value is not of type \(T.self) and not even \(U.self)"))
        }

    }
}
公共结构不确定值:可解码{
公共价值:T?
公共价值:U?
公共var值:有吗{
返回tValue??uValue
}
public init(来自解码器:解码器)抛出{
let container=尝试解码器。singleValueContainer()
tValue=try?container.decode(T.self)
uValue=try?container.decode(U.self)
如果tValue==nil&&v值==nil{
//类型不匹配
抛出DecodingError.typeMismatch(类型(of:self)、DecodingError.Context(编码路径:[],调试说明:“值不是\(T.self)类型,甚至不是\(U.self)”)
}
}
}
从现在起,您的Person对象将是

struct Person: Decodable {
    var id: UncertainValue<Int, String>
}
struct-Person:可解码{
变量id:不确定值
}

您将能够使用id.value访问您的id

有一个角落案例不在Luca Angeletti的解决方案中

例如,如果Cordinate的类型是Double或[Double],Angeletti的解决方案将导致一个错误:“应该解码Double,但找到了一个数组”

在这种情况下,您必须在Cordinate中使用嵌套枚举

enum Cordinate: Decodable {
    case double(Double), array([Cordinate])

    init(from decoder: Decoder) throws {
        if let double = try? decoder.singleValueContainer().decode(Double.self) {
            self = .double(double)
            return
        }

        if let array = try? decoder.singleValueContainer().decode([Cordinate].self) {
            self = .array(array)
            return
        }

        throw CordinateError.missingValue
    }

    enum CordinateError: Error {
        case missingValue
    }
}

struct Geometry : Decodable {
    let date : String?
    let type : String?
    let coordinates : [Cordinate]?

    enum CodingKeys: String, CodingKey {

        case date = "date"
        case type = "type"
        case coordinates = "coordinates"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        date = try values.decodeIfPresent(String.self, forKey: .date)
        type = try values.decodeIfPresent(String.self, forKey: .type)
        coordinates = try values.decodeIfPresent([Cordinate].self, forKey: .coordinates)
    }
}

只需使用Matt Thompson的酷库中的
AnyCodable
类型即可

例如:


要想成为任何一个关键点,我喜欢以上所有答案。但当您不确定服务器人员将发送哪种数据类型时,您就使用Quantum类(如上所述),但Quantum类型不难使用或管理。因此,这里是我的解决方案,使您的可解码类密钥成为任何数据类型(或obj-c爱好者的“id”)

用法:

let json = "{\"success\":\"hii\"}".data(using: .utf8) // response will be String
        //let json = "{\"success\":50.55}".data(using: .utf8)  //response will be Double
        //let json = "{\"success\":50}".data(using: .utf8) //response will be Int
        let decoded = try? JSONDecoder().decode(StatusResp.self, from: json!)
        print(decoded?.success) // It will print Any

        if let doubleValue = decoded?.success as? Double {

        }else if let doubleValue = decoded?.success as? Int {

        }else if let doubleValue = decoded?.success as? String {

        }

在这里,您的
id
可以是任何
Codable
类型:

Swift 4.2
struct Person:Codable{
变量id:T
变量名称:字符串?
}
设p1=人(id:1,姓名:“账单”)
让p2=人(id:“一”,姓名:“约翰”)

多亏了Luka Angeletti的回答(),我将enum改为struct,这样我们可以更轻松地使用它

struct QuantumValue: Codable {

    public var string: String?
    public var integer: Int?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let int = try? container.decode(Int.self) {
            self.integer = int
            return
        }
        if let string = try? container.decode(String.self) {
            self.string = string
            return
        }
        throw QuantumError.missingValue
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(string)
        try container.encode(integer)
    }

    enum QuantumError: Error {
         case missingValue
    }

    func value() -> Any? {
        if let s = string {
            return s
        }
        if let i = integer {
            return i
        }
        return nil
    }
}

在您使用泛型的方法中,我必须仍然知道我从
JSON
获得的
id
的数据类型