Swift JSONECODER';s日期编码策略不起作用
我正在尝试使用Swift 4的Encodable+JSONEncoder将结构序列化为字符串。对象可以保存异类值,如字符串、数组、日期、Int等 所使用的方法效果良好,但日期除外。 JSONECODER的Swift JSONECODER';s日期编码策略不起作用,swift,serialization,encodable,jsonencoder,Swift,Serialization,Encodable,Jsonencoder,我正在尝试使用Swift 4的Encodable+JSONEncoder将结构序列化为字符串。对象可以保存异类值,如字符串、数组、日期、Int等 所使用的方法效果良好,但日期除外。 JSONECODER的dateEncodingStrategy属性没有任何效果 下面是一个片段,它再现了在操场上的行为: struct EncodableValue:Encodable { var value: Encodable init(_ value: Encodable) {
dateEncodingStrategy
属性没有任何效果
下面是一个片段,它再现了在操场上的行为:
struct EncodableValue:Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
try value.encode(to: encoder)
}
}
struct Bar: Encodable, CustomStringConvertible {
let key: String?
let value: EncodableValue?
var description: String {
let encoder = JSONEncoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E, d MMM yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
encoder.dateEncodingStrategy = .formatted(dateFormatter)
let jsonData = try? encoder.encode(self)
return String(data: jsonData!, encoding: .utf8)!
}
}
let bar1 = Bar(key: "bar1", value: EncodableValue("12345"))
let bar2 = Bar(key: "bar2", value: EncodableValue(12345))
let bar3 = Bar(key: "bar3", value: EncodableValue(Date()))
print(String(describing: bar1))
print(String(describing: bar2))
print(String(describing: bar3))
输出:
"{"key":"bar1","value":"12345"}\n"
"{"key":"bar2","value":12345}\n"
"{"key":"bar3","value":539682026.06086397}\n"
对于bar3对象:我期望类似于“{”key:“bar3”,“value:“Thurs,1991年1月3日”}\n“
,但它以默认的.deferToDate策略格式返回日期
###编辑1##
因此,我在XCode 9中运行了相同的代码,它给出了预期的输出,即正确地将日期格式化为字符串。
我认为9.2对Swift 4进行了一次小升级,打破了这一特性。不知道下一步该怎么办
###编辑2##
作为一种临时补救措施,我在使用@Hamish的闭包方法之前使用了以下代码段
struct EncodableValue:Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
if let date = value as? Date {
var container = encoder.singleValueContainer()
try container.encode(date)
}
else {
try value.encode(to: encoder)
}
}
}
您可以消除
encodalevalue
包装,改为使用泛型:
struct Bar<T: Encodable>: Encodable {
let key: String
let value: T?
var json: String {
let encoder = JSONEncoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E, d MMM yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
encoder.dateEncodingStrategy = .formatted(dateFormatter)
let data = try! encoder.encode(self)
return String(data: data, encoding: .utf8)!
}
}
let bar = Bar(key: "date", value: Date())
print(bar.json)
结构栏:可编码{
让键:字符串
让值:T?
var-json:String{
let encoder=JSONEncoder()
让dateFormatter=dateFormatter()
dateFormatter.dateFormat=“E,d MMM yyy”
dateFormatter.locale=locale(标识符:“en_US_POSIX”)
编码器.dateEncodingStrategy=.formatted(日期格式化程序)
让data=try!encoder.encode(self)
返回字符串(数据:数据,编码:.utf8)!
}
}
设bar=bar(键:“日期”,值:date())
打印(bar.json)
这将产生:
{"key":"date","value":"Wed, 7 Feb 2018"}
{“key”:“date”,“value”:“Wed,2018年2月7日”}
当使用自定义日期编码策略时,编码器将在给定容器中对
日期
进行编码,然后
但是,使用encodalevelue
包装器,您没有给编码器这样做的机会,因为您直接调用底层值的encode(to:)
方法。使用日期
,这是它的
要解决这个问题,您需要在单个值容器中对基础值进行编码,以触发任何自定义编码策略。执行此操作的唯一障碍是这样一个事实,即您不能使用可编码的参数调用容器的编码(:)
方法(因为参数采用
)
此问题的一个解决方案是定义一个可编码的
扩展,用于编码到单个值容器中,然后可以在包装器中使用该容器:
extension Encodable {
fileprivate func encode(to container: inout SingleValueEncodingContainer) throws {
try container.encode(self)
}
}
struct AnyEncodable : Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try value.encode(to: &container)
}
}
这利用了这样一个事实,即协议扩展成员有一个隐式的
占位符,其中p
是被扩展的协议,隐式的self
参数被键入为这个占位符(长话短说;它允许我们调用编码(:)
具有可编码的方法(符合类型)
另一种选择是在包装器上有一个通用的初始化器,它通过存储一个执行编码的闭包来进行类型擦除:
struct AnyEncodable : Encodable {
private let _encodeTo: (Encoder) throws -> Void
init<Value : Encodable>(_ value: Value) {
self._encodeTo = { encoder in
var container = encoder.singleValueContainer()
try container.encode(value)
}
}
func encode(to encoder: Encoder) throws {
try _encodeTo(encoder)
}
}
我已经在Swift 4游乐场检查了你的代码,它给了我关于bar3的结果:{“键”:“bar3”,“值”:“Wed,2018年2月7日”}@ArunKumar-这很奇怪,因为我精确地看到了justintime在使用EncodeValue
包装器时描述的行为。@ArunKumar你添加了任何额外的代码或更改来纠正这个问题吗?至于我,我也得到了同样的回答。@ShaneD nothing。。。我只是复制了你的代码并粘贴了进去playground@ArunKumar你的XCode版本是什么?我使用的是XCode 9.2,我刚刚运行了XCode 9,它在那里工作。实际上,在更大的画面中,我在另一个结构中使用了Bar作为属性<代码>结构Foo{var bar:[bar]}
,这是我想要序列化的实际结构。如果使用泛型方法,我将无法指定具体类型。最后我会讨论你提出的这个方法的老问题。希望我说的有道理。在这种情况下,假设您不能使用另一个答案中概述的选项2(使用强类型struct
而不是异构字典或数组集合),那么您可以使用Hamish的方法。我猜这是可行的,因为我们使用泛型来延迟使用运行时提供的值进行编码。因为我尝试了func encode(to encoder:encoder)抛出{
try container.encode(value)
}
,这导致了错误“无法使用类型为“(Encodable)”的参数列表调用“encode”。干净利落,谢谢@justintime实际上只是想到了一种可能更简洁的方法来使用扩展名:)是的,如果您试图用一个可编码的参数调用编码(u:)
方法,它将不起作用,因为这些方法的形式是(u值:值)
,而且,Encodable
目前无法满足这样一个通用占位符的要求,因为。为什么更喜欢扩展方法?@justintime它只是更灵活;对于受约束的泛型初始化器,您需要知道调用站点的具体类型,但是对于使用扩展名的包装器,您可以说let encodables=[1,“hello”,Date()].map(AnyEncodable.init)
:)
import Foundation
struct Bar : Encodable, CustomStringConvertible {
let key: String
let value: AnyEncodable
var description: String {
let encoder = JSONEncoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E, d MMM yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
encoder.dateEncodingStrategy = .formatted(dateFormatter)
guard let jsonData = try? encoder.encode(self) else {
return "Bar(key: \(key as Any), value: \(value as Any))"
}
return String(decoding: jsonData, as: UTF8.self)
}
}
print(Bar(key: "bar1", value: AnyEncodable("12345")))
// {"key":"bar1","value":"12345"}
print(Bar(key: "bar2", value: AnyEncodable(12345)))
// {"key":"bar2","value":12345}
print(Bar(key: "bar3", value: AnyEncodable(Date())))
// {"key":"bar3","value":"Wed, 7 Feb 2018"}