我正在尝试使用swift 4的可编码+jsonEncoder将结构序列化为字符串。对象可以保存不同的值,如字符串、数组、日期、int等。
除日期外,使用的方法工作正常。
jsonEncoder的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, 3 Jan 1991"}\n"的内容,但它以默认的.defertodate策略格式返回日期。
##编辑1##
所以我在Xcode9中运行了相同的代码,它给出了预期的输出,也就是正确地将日期格式化为字符串。
我想9.2有一个小的升级到Swift4,这打破了这个功能。不知道下一步该怎么做。
##编辑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)
        }

    }
}

最佳答案

当使用自定义日期编码策略时,编码器intercepts calls将给定容器中的Date编码,然后applies the custom strategy
但是,使用您的EncodableValue包装器,您不会给编码器这样做的机会,因为您直接调用底层值的encode(to:)方法。对于Date,这个will encode the value using its default representation,就是它的timeIntervalSinceReferenceDate
要解决此问题,需要在单个值容器中对基础值进行编码,以触发任何自定义编码策略。这样做的唯一障碍是protocols don't conform to themselves这一事实,因此您不能使用encode(_:)参数调用容器的Encodable方法(因为参数采用的是<Value : Encodable>)。
此问题的一个解决方案是定义一个用于编码到单个值容器的Encodable扩展,然后可以在包装器中使用该扩展:

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)
  }
}

这充分利用了这样一个事实:协议扩展成员有一个隐式的<Self : P>占位符,其中P是要扩展的协议,隐式的self参数被类型化为这个占位符(长话短说;它允许我们用一个符合encode(_:)的类型调用Encodable方法)。
另一种选择是在包装上有一个通用的初始化器,通过存储执行编码的闭包来键入擦除:
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)
  }
}

在这两种情况下,您现在都可以使用这个包装器来对异源代码进行编码,同时尊重定制的编码策略:
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"}

关于swift - JSONEncoder dateEncodingStrategy无法正常工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48658574/

10-12 14:32