I'm currently working with Codable
types in my project and facing an issue.
我目前正在处理项目中的Codable类型并遇到问题。
struct Person: Codable
{
var id: Any
}
id
in the above code could be either a String
or an Int
. This is the reason id
is of type Any
.
上面代码中的id可以是String或Int。这是id为Any类型的原因。
I know that Any
is not Codable
.
我知道Any不是Codable。
What I need to know is how can I make it work.
我需要知道的是我如何才能使它发挥作用。
8 个解决方案
#1
13
Codable needs to know the type to cast to.
Codable需要知道要转换的类型。
Firstly I would try to address the issue of not knowing the type, see if you can fix that and make it simpler.
首先,我会尝试解决不知道类型的问题,看看你是否可以修复它并使其更简单。
Otherwise the only way I can think of solving your issue currently is to use generics like below.
否则,我现在想到解决问题的唯一方法就是使用如下的泛型。
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")
#2
10
Quantum Value
First of all you can define a type that can be decoded both from a String
and Int
value. Here it is.
首先,您可以定义一个可以从String和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
}
}
Person
Now you can define your struct like this
现在您可以像这样定义结构
struct Person: Decodable {
let id: QuantumValue
}
That's it. Let's test it!
而已。我们来试试吧!
JSON 1: id
is String
let data = """
{
"id": "123"
}
""".data(using: String.Encoding.utf8)!
if let person = try? JSONDecoder().decode(Person.self, from: data) {
print(person)
}
JSON 2: id
is Int
let data = """
{
"id": 123
}
""".data(using: String.Encoding.utf8)!
if let person = try? JSONDecoder().decode(Person.self, from: data) {
print(person)
}
#3
6
I solved this issue defining a new Decodable Struct called AnyDecodable, so instead of Any I use AnyDecodable. It works perfectly also with nested types.
我解决了这个问题,定义了一个名为AnyDecodable的新Decodable Struct,因此我使用AnyDecodable而不是Any。它也适用于嵌套类型。
Try this in a playground:
在游乐场试试这个:
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)
You could extend my struct to be AnyCodable if you are interested also in the Encoding part.
如果您对编码部分感兴趣,可以将我的结构扩展为AnyCodable。
Edit: I actually did it.
编辑:我实际上做到了。
Here is AnyCodable
这是AnyCodable
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"))
}
}
}
}
You can test it With the previous json in this way in a playground:
你可以在游乐场用这种方式测试它以前的json:
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)
#4
2
You can replace Any
with an enum accepting an Int
or a String
:
您可以使用接受Int或String的枚举替换Any:
enum Id: Codable {
case numeric(value: Int)
case named(name: String)
}
struct Person: Codable
{
var id: Id
}
Then the compiler will complain about the fact that Id
does not conform to Decodable
. Because Id
has associated values you need to implement this yourself. Read https://littlebitesofcocoa.com/318-codable-enums for an example of how to do this.
然后编译器会抱怨Id不符合Decodable的事实。由于Id具有关联值,因此您需要自己实现。请阅读https://littlebitesofcocoa.com/318-codable-enums,了解如何执行此操作的示例。
#5
2
If your problem is that it's uncertain the type of id as it might be either a string or an integer value, I can suggest you this blog post: http://agostini.tech/2017/11/12/swift-4-codable-in-real-life-part-2/
如果你的问题是id的类型不确定,因为它可能是字符串或整数值,我可以建议你这篇博文:http://agostini.tech/2017/11/12/swift-4-codable -in-现实生活部分-2 /
Basically I defined a new Decodable type
基本上我定义了一个新的Decodable类型
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)"))
}
}
}
From now on, your Person object would be
从现在开始,您的Person对象将是
struct Person: Decodable {
var id: UncertainValue<Int, String>
}
you will be able to access your id using id.value
您将能够使用id.value访问您的ID
#6
1
First of all, as you can read in other answers and comments, using Any
for this is not good design. If possible, give it a second thought.
首先,正如您可以阅读其他答案和评论,使用Any对此并不是一个好的设计。如果可能的话,再考虑一下。
That said, if you want to stick to it for your own reasons, you should write your own encoding/decoding and adopt some kind of convention in the serialized JSON.
也就是说,如果你想坚持自己的原因,你应该编写自己的编码/解码,并在序列化的JSON中采用某种约定。
The code below implements it by encoding id
always as string and decoding to Int
or String
depending on the found value.
下面的代码通过将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)>" }
}
Examples
Encode object with numeric id
:
用数字id编码对象:
var p1 = Person(id: 1)
print(String(data: try JSONEncoder().encode(p1),
encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"1"}
Encode object with string id
:
用字符串id编码对象:
var p2 = Person(id: "root")
print(String(data: try JSONEncoder().encode(p2),
encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"root"}
Decode to numeric id
:
解码为数字ID:
print(try JSONDecoder().decode(Person.self,
from: "{\"id\": \"2\"}".data(using: String.Encoding.utf8)!))
// <Person id:2>
Decode to string id
:
解码为字符串ID:
print(try JSONDecoder().decode(Person.self,
from: "{\"id\": \"admin\"}".data(using: String.Encoding.utf8)!))
// <Person id:admin>
An alternative implementation would be encoding to Int
or String
and wrap the decoding attempts in a do...catch
.
另一种实现方式是对Int或String进行编码,并将解码尝试包装在do ... catch中。
In the encoding part:
在编码部分:
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)
}
And then decode to the right type in multiple attempts:
然后在多次尝试中解码为正确的类型:
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 */
}
}
It's uglier in my opinion.
在我看来这很丑陋。
Depending on the control you have over the server serialization you can use either of them or write something else adapted to the actual serialization.
根据您对服务器序列化的控制,您可以使用其中任何一个或编写适合实际序列化的其他内容。
#7
0
There is a corner case which is not covered by Luca Angeletti's solution.
Luca Angeletti的解决方案没有涵盖角落案例。
For instance, if Cordinate's type is Double or [Double], Angeletti's solution will cause an error: "Expected to decode Double but found an array instead"
例如,如果Cordinate的类型是Double或[Double],Angeletti的解决方案将导致错误:“预计解码Double但发现了一个数组”
In this case, you have to use nested enum instead in Cordinate.
在这种情况下,您必须在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)
}
}
#8
0
Simply you can use AnyCodable
type from Matt Thompson's cool library AnyCodable.
您可以使用Matt Thompson的酷库AnyCodable中的AnyCodable类型。
Eg:
例如:
import AnyCodable
struct Person: Codable
{
var id: AnyCodable
}
#1
13
Codable needs to know the type to cast to.
Codable需要知道要转换的类型。
Firstly I would try to address the issue of not knowing the type, see if you can fix that and make it simpler.
首先,我会尝试解决不知道类型的问题,看看你是否可以修复它并使其更简单。
Otherwise the only way I can think of solving your issue currently is to use generics like below.
否则,我现在想到解决问题的唯一方法就是使用如下的泛型。
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")
#2
10
Quantum Value
First of all you can define a type that can be decoded both from a String
and Int
value. Here it is.
首先,您可以定义一个可以从String和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
}
}
Person
Now you can define your struct like this
现在您可以像这样定义结构
struct Person: Decodable {
let id: QuantumValue
}
That's it. Let's test it!
而已。我们来试试吧!
JSON 1: id
is String
let data = """
{
"id": "123"
}
""".data(using: String.Encoding.utf8)!
if let person = try? JSONDecoder().decode(Person.self, from: data) {
print(person)
}
JSON 2: id
is Int
let data = """
{
"id": 123
}
""".data(using: String.Encoding.utf8)!
if let person = try? JSONDecoder().decode(Person.self, from: data) {
print(person)
}
#3
6
I solved this issue defining a new Decodable Struct called AnyDecodable, so instead of Any I use AnyDecodable. It works perfectly also with nested types.
我解决了这个问题,定义了一个名为AnyDecodable的新Decodable Struct,因此我使用AnyDecodable而不是Any。它也适用于嵌套类型。
Try this in a playground:
在游乐场试试这个:
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)
You could extend my struct to be AnyCodable if you are interested also in the Encoding part.
如果您对编码部分感兴趣,可以将我的结构扩展为AnyCodable。
Edit: I actually did it.
编辑:我实际上做到了。
Here is AnyCodable
这是AnyCodable
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"))
}
}
}
}
You can test it With the previous json in this way in a playground:
你可以在游乐场用这种方式测试它以前的json:
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)
#4
2
You can replace Any
with an enum accepting an Int
or a String
:
您可以使用接受Int或String的枚举替换Any:
enum Id: Codable {
case numeric(value: Int)
case named(name: String)
}
struct Person: Codable
{
var id: Id
}
Then the compiler will complain about the fact that Id
does not conform to Decodable
. Because Id
has associated values you need to implement this yourself. Read https://littlebitesofcocoa.com/318-codable-enums for an example of how to do this.
然后编译器会抱怨Id不符合Decodable的事实。由于Id具有关联值,因此您需要自己实现。请阅读https://littlebitesofcocoa.com/318-codable-enums,了解如何执行此操作的示例。
#5
2
If your problem is that it's uncertain the type of id as it might be either a string or an integer value, I can suggest you this blog post: http://agostini.tech/2017/11/12/swift-4-codable-in-real-life-part-2/
如果你的问题是id的类型不确定,因为它可能是字符串或整数值,我可以建议你这篇博文:http://agostini.tech/2017/11/12/swift-4-codable -in-现实生活部分-2 /
Basically I defined a new Decodable type
基本上我定义了一个新的Decodable类型
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)"))
}
}
}
From now on, your Person object would be
从现在开始,您的Person对象将是
struct Person: Decodable {
var id: UncertainValue<Int, String>
}
you will be able to access your id using id.value
您将能够使用id.value访问您的ID
#6
1
First of all, as you can read in other answers and comments, using Any
for this is not good design. If possible, give it a second thought.
首先,正如您可以阅读其他答案和评论,使用Any对此并不是一个好的设计。如果可能的话,再考虑一下。
That said, if you want to stick to it for your own reasons, you should write your own encoding/decoding and adopt some kind of convention in the serialized JSON.
也就是说,如果你想坚持自己的原因,你应该编写自己的编码/解码,并在序列化的JSON中采用某种约定。
The code below implements it by encoding id
always as string and decoding to Int
or String
depending on the found value.
下面的代码通过将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)>" }
}
Examples
Encode object with numeric id
:
用数字id编码对象:
var p1 = Person(id: 1)
print(String(data: try JSONEncoder().encode(p1),
encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"1"}
Encode object with string id
:
用字符串id编码对象:
var p2 = Person(id: "root")
print(String(data: try JSONEncoder().encode(p2),
encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"root"}
Decode to numeric id
:
解码为数字ID:
print(try JSONDecoder().decode(Person.self,
from: "{\"id\": \"2\"}".data(using: String.Encoding.utf8)!))
// <Person id:2>
Decode to string id
:
解码为字符串ID:
print(try JSONDecoder().decode(Person.self,
from: "{\"id\": \"admin\"}".data(using: String.Encoding.utf8)!))
// <Person id:admin>
An alternative implementation would be encoding to Int
or String
and wrap the decoding attempts in a do...catch
.
另一种实现方式是对Int或String进行编码,并将解码尝试包装在do ... catch中。
In the encoding part:
在编码部分:
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)
}
And then decode to the right type in multiple attempts:
然后在多次尝试中解码为正确的类型:
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 */
}
}
It's uglier in my opinion.
在我看来这很丑陋。
Depending on the control you have over the server serialization you can use either of them or write something else adapted to the actual serialization.
根据您对服务器序列化的控制,您可以使用其中任何一个或编写适合实际序列化的其他内容。
#7
0
There is a corner case which is not covered by Luca Angeletti's solution.
Luca Angeletti的解决方案没有涵盖角落案例。
For instance, if Cordinate's type is Double or [Double], Angeletti's solution will cause an error: "Expected to decode Double but found an array instead"
例如,如果Cordinate的类型是Double或[Double],Angeletti的解决方案将导致错误:“预计解码Double但发现了一个数组”
In this case, you have to use nested enum instead in Cordinate.
在这种情况下,您必须在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)
}
}
#8
0
Simply you can use AnyCodable
type from Matt Thompson's cool library AnyCodable.
您可以使用Matt Thompson的酷库AnyCodable中的AnyCodable类型。
Eg:
例如:
import AnyCodable
struct Person: Codable
{
var id: AnyCodable
}