可解码和 JSON,同一变量的 2 种数据类型
Decodable and JSON, 2 datatypes for same variable
我正在使用 Decodable 协议解码一些 json,但我 运行 遇到了问题:
我得到了回复,其中经度和纬度可以是整数(纬度 = 0)(如果没有向元素添加地理位置数据)和字符串(fx.latitude = "25.047880 ") 如果有可用的地理数据。现在当我解码 json 时,我不知道如何构建我的 Struct,因为 long 和 lat 不能同时是 String 和 Int。所以我在获取元素时遇到解码错误代表案例。
关于如何解决这个问题有什么建议吗?我尝试使用 "Any" 作为数据类型,但这不符合 Decodable 协议
struct JPhoto: Decodable {
let id: String
let farm: Int
let secret: String
let server: String
let owner: String
let title: String
let latitude: String //Can both be Int and String
let longitude: String //Can both be Int and String
}
您需要自己编写 encoder/decoder。您可以使用关联值枚举来执行此操作,使用 switch 语句进行编码并使用 throwing/catching 行为进行解码:
enum AngularDistance:Codable {
case string(String), integer(Int)
func encode(to encoder: Encoder) throws {
switch self {
case .string(let str):
var container = encoder.singleValueContainer()
try container.encode(str)
case .integer(let int):
var container = encoder.singleValueContainer()
try container.encode(int)
}
}
init(from decoder: Decoder) throws {
do {
let container = try decoder.singleValueContainer()
let str = try container.decode(String.self)
self = AngularDistance.string(str)
}
catch {
do { let container = try decoder.singleValueContainer()
let int = try container.decode(Int.self)
self = AngularDistance.integer(int)
}
catch {
throw DecodingError.typeMismatch(AngularDistance.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected to decode an Int or a String"))
}
}
}
}
下面是编码和解码此 AngularDistance
类型的示例:
let lat = [AngularDistance.string("String"), AngularDistance.integer(10)]
let encoder = JSONEncoder()
var decoder = JSONDecoder()
do {
let encoded = try encoder.encode(lat)
try decoder.decode(Array<AngularDistance>.self, from: encoded)
}
catch DecodingError.typeMismatch(let t, let e) {
t
e.codingPath
e.debugDescription
}
catch {
print(error.localizedDescription)
}
这是重写的结构:
struct JPhoto: Decodable {
let id: String
let farm: Int
let secret: String
let server: String
let owner: String
let title: String
let latitude: AngularDistance //Can both be Int and String
let longitude: AngularDistance //Can both be Int and String
}
除了具有关联值的枚举之外,还有几种方法值得一提。您可以为 latitude
和 longitude
.
使用建议的 Either<Int, String>
或 IntOrString
结构
struct Either<F: Codable, S: Codable>: Codable {
var firstValue: F?
var secondValue: S?
var value: Any? {
return firstValue ?? secondValue
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if firstValue != nil {
try? container.encode(firstValue)
} else {
try? container.encode(secondValue)
}
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
firstValue = try? container.decode(F.self)
secondValue = try? container.decode(S.self)
if firstValue == nil && secondValue == nil {
//Type mismatch
throw DecodingError.typeMismatch(type(of: self), DecodingError.Context(codingPath: [], debugDescription: "The value is not of type \(F.self) and also not \(S.self)"))
}
}
}
另一种方法:
struct IntOrString: Codable {
var value: Any
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let intValue = value as? Int {
try? container.encode(intValue)
} else if let strValue = value as? String {
try? container.encode(strValue)
}
}
init(from decoder: Decoder) throws {
if let int = try? Int(from: decoder) {
value = int
return
}
value = try String(from: decoder)
}
}
我正在使用 Decodable 协议解码一些 json,但我 运行 遇到了问题:
我得到了回复,其中经度和纬度可以是整数(纬度 = 0)(如果没有向元素添加地理位置数据)和字符串(fx.latitude = "25.047880 ") 如果有可用的地理数据。现在当我解码 json 时,我不知道如何构建我的 Struct,因为 long 和 lat 不能同时是 String 和 Int。所以我在获取元素时遇到解码错误代表案例。
关于如何解决这个问题有什么建议吗?我尝试使用 "Any" 作为数据类型,但这不符合 Decodable 协议
struct JPhoto: Decodable {
let id: String
let farm: Int
let secret: String
let server: String
let owner: String
let title: String
let latitude: String //Can both be Int and String
let longitude: String //Can both be Int and String
}
您需要自己编写 encoder/decoder。您可以使用关联值枚举来执行此操作,使用 switch 语句进行编码并使用 throwing/catching 行为进行解码:
enum AngularDistance:Codable {
case string(String), integer(Int)
func encode(to encoder: Encoder) throws {
switch self {
case .string(let str):
var container = encoder.singleValueContainer()
try container.encode(str)
case .integer(let int):
var container = encoder.singleValueContainer()
try container.encode(int)
}
}
init(from decoder: Decoder) throws {
do {
let container = try decoder.singleValueContainer()
let str = try container.decode(String.self)
self = AngularDistance.string(str)
}
catch {
do { let container = try decoder.singleValueContainer()
let int = try container.decode(Int.self)
self = AngularDistance.integer(int)
}
catch {
throw DecodingError.typeMismatch(AngularDistance.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected to decode an Int or a String"))
}
}
}
}
下面是编码和解码此 AngularDistance
类型的示例:
let lat = [AngularDistance.string("String"), AngularDistance.integer(10)]
let encoder = JSONEncoder()
var decoder = JSONDecoder()
do {
let encoded = try encoder.encode(lat)
try decoder.decode(Array<AngularDistance>.self, from: encoded)
}
catch DecodingError.typeMismatch(let t, let e) {
t
e.codingPath
e.debugDescription
}
catch {
print(error.localizedDescription)
}
这是重写的结构:
struct JPhoto: Decodable {
let id: String
let farm: Int
let secret: String
let server: String
let owner: String
let title: String
let latitude: AngularDistance //Can both be Int and String
let longitude: AngularDistance //Can both be Int and String
}
除了具有关联值的枚举之外,还有几种方法值得一提。您可以为 latitude
和 longitude
.
Either<Int, String>
或 IntOrString
结构
struct Either<F: Codable, S: Codable>: Codable {
var firstValue: F?
var secondValue: S?
var value: Any? {
return firstValue ?? secondValue
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if firstValue != nil {
try? container.encode(firstValue)
} else {
try? container.encode(secondValue)
}
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
firstValue = try? container.decode(F.self)
secondValue = try? container.decode(S.self)
if firstValue == nil && secondValue == nil {
//Type mismatch
throw DecodingError.typeMismatch(type(of: self), DecodingError.Context(codingPath: [], debugDescription: "The value is not of type \(F.self) and also not \(S.self)"))
}
}
}
另一种方法:
struct IntOrString: Codable {
var value: Any
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if let intValue = value as? Int {
try? container.encode(intValue)
} else if let strValue = value as? String {
try? container.encode(strValue)
}
}
init(from decoder: Decoder) throws {
if let int = try? Int(from: decoder) {
value = int
return
}
value = try String(from: decoder)
}
}