[英]Swift - How to make JSONDecoder correctly parse Date array in hierarchy of classes
我有两个类,一个扩展了另一个类(请参见下面的DateDemo
和DateDemo2
)。 如果孩子中有[Date]
,则JSONDecoder无法正确解析它。
这是我在iOS上运行的测试类DateDecodeTests
。 基本上,我只是尝试使用testDateDecodingWithHierarchy
测试方法来传递返回与nil
不同的eventsDateTimes3
。
从这两个测试中可以看出, testDateDecoding
可以完美地工作,相反, testDateDecodingWithHierarchy
在解析eventsDateTimes3
字段(该字段仅由子类DateDemo2
)中存在问题。
import Foundation
import XCTest
class DateDecodeTests: XCTestCase {
class DateDemo :Decodable {
var dates : [Date]
var eventsDateTimes : [Date]?
var eventsDateTimes2 : [Date]?
}
class DateDemo2 : DateDemo {
var eventsDateTimes3 : [Date]?
}
func testDateDecoding() throws {
let json = """
{
"dates": ["2018-10-17T23:00:00.000+01:00", "2018-10-18T00:30:00.000+01:00"],
"eventsDateTimes": ["2018-10-18T22:00:00.000+02:00", "2018-10-31T00:30:00.000+01:00", "2018-11-08T20:00:00.000+01:00"],
"eventsDateTimes2": [],
}
""".data(using: .utf8)!
let decoder : JSONDecoder = JSONDecoder.myDefaultJsonDecoder
let date = try decoder.decode(DateDemo.self, from: json)
print(date.dates)
print(date.eventsDateTimes)
print(date.eventsDateTimes2)
// It prints out:
// [2018-10-17 22:00:00 +0000, 2018-10-17 23:30:00 +0000]
// Optional([2018-10-18 20:00:00 +0000, 2018-10-30 23:30:00 +0000, 2018-11-08 19:00:00 +0000])
// Optional([])
}
func testDateDecodingWithHierarchy() throws {
let json = """
{
"dates": ["2018-10-17T23:00:00.000+01:00", "2018-10-18T00:30:00.000+01:00"],
"eventsDateTimes": ["2018-10-18T22:00:00.000+02:00", "2018-10-31T00:30:00.000+01:00", "2018-11-08T20:00:00.000+01:00"],
"eventsDateTimes2": [],
"eventsDateTimes3": ["2018-10-18T22:00:00.000+02:00", "2018-10-31T00:30:00.000+01:00", "2018-11-08T20:00:00.000+01:00"],
}
""".data(using: .utf8)!
let decoder : JSONDecoder = JSONDecoder.myDefaultJsonDecoder
let date = try decoder.decode(DateDemo2.self, from: json)
print(date.dates)
print(date.eventsDateTimes)
print(date.eventsDateTimes2)
print(date.eventsDateTimes3)
// It prints out:
// [2018-10-17 22:00:00 +0000, 2018-10-17 23:30:00 +0000]
// Optional([2018-10-18 20:00:00 +0000, 2018-10-30 23:30:00 +0000, 2018-11-08 19:00:00 +0000])
// Optional([])
// nil ---> ??? Why is this nil?
assert(date.eventsDateTimes3 != nil, "eventsDateTimes3 must not be nil")
}
}
extension JSONDecoder {
static var myDefaultJsonDecoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.myDefaultDateFormatter)
return decoder
}
}
extension DateFormatter {
static var myDefaultDateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ"
return formatter
}
}
DateDemo
符合Decodable
,但未明确定义init(from:Decoder) throws
。 因此Swift合成了一个init(from:Decoder) throws
的定义,该定义对DateDemo
的属性进行DateDemo
。
DateDemo2
从DateDemo
继承。 它添加了一个属性: eventsDateTimes3
。 它没有定义任何初始化程序。 通常,Swift会抱怨缺少初始化器。 但是,由于new属性既是var
又是可选属性,因此Swift认为它的默认值为nil
。
由于DateDemo2
的唯一新属性具有默认值,因此Swift将应用自动初始化程序继承 。 DateDemo2
类继承了其超类的所有指定初始化器。 只能继承一个初始化程序:合成的Decodable
初始化程序。 继承的初始化程序将eventsDateTimes3
初始化为其默认值( nil
)。 这就是继承的初始化程序的工作方式。
你希望斯威夫特会合成一个Decodable
初始化为DateDemo2
因为它没有为DateDemo
,通过初始化eventsDateTimes3
从Decoder
。 但是Swift并没有这样做。 这是Itai Ferber(负责Coding
实现的Apple程序员)在Swift论坛上说的:
如果编译器可以合成
SuperClass.init(from:)
而不是继承,则这种情况将得到改善,但是如果没有重构Swift的协议一致性和继承系统(并且没有语法来消除“我是因为我想继承而没有提供实现”与“因为我想合成而没有提供实现”)
换句话说,有两种合法的情况:
您现在总是得到#1,因为这就是添加Codable
系统之前的Codable
,并且因为没有语法告诉编译器您想要#2。
因此,您需要在DateDemo2
自己实现init(from:)
。 这是一个实现:
class DateDemo2 : DateDemo {
var eventsDateTimes3 : [Date]?
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
eventsDateTimes3 = try container.decode([Date]?.self, forKey: .eventsDateTimes3)
try super.init(from: decoder)
}
private enum CodingKeys: String, CodingKey {
case eventsDateTimes3
}
}
这取决于您的用例,但是另一种解决方法是将通用属性放入协议中,并使用它代替继承。 (还允许您使用结构😉)
protocol AnyDateHolder: Decodable {
var dates : [Date] {get set}
var eventsDateTimes : [Date]? {get set}
var eventsDateTimes2 : [Date]? {get set}
}
class DateDemo: AnyDateHolder {
var dates : [Date]
var eventsDateTimes : [Date]?
var eventsDateTimes2 : [Date]?
}
class DateDemo2: AnyDateHolder {
var dates : [Date]
var eventsDateTimes : [Date]?
var eventsDateTimes2 : [Date]?
var eventsDateTimes3 : [Date]?
}
在第二次测试中,它会打印:
[2018-10-17 22:00:00 +0000, 2018-10-17 23:30:00 +0000]
Optional([2018-10-18 20:00:00 +0000, 2018-10-30 23:30:00 +0000, 2018-11-08 19:00:00 +0000])
Optional([])
Optional([2018-10-18 20:00:00 +0000, 2018-10-30 23:30:00 +0000, 2018-11-08 19:00:00 +0000])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.