首先,正如您可以在其他答案和 cmets 中看到的那样,使用 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"}
用字符串id编码对象:
var p2 = Person(id: "root")
print(String(data: try JSONEncoder().encode(p2),
encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"root"}
解码为数字id:
print(try JSONDecoder().decode(Person.self,
from: "{\"id\": \"2\"}".data(using: String.Encoding.utf8)!))
// <Person id:2>
解码成字符串id:
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 */
}
}
在我看来它更丑。
根据您对服务器序列化的控制,您可以使用它们中的任何一个或编写适合实际序列化的其他内容。