JSONEncoder 的 dateEncodingStrategy 不起作用

2023-12-04

我正在尝试使用 Swift 4 的 Encodable+JSONEncoder 将结构序列化为字符串。该对象可以保存异构值,例如 String、Array、Date、Int 等。

除了日期之外,所使用的方法工作正常。 JSON编码器的dateEncodingStrategy属性没有任何影响。

以下是重现 Playground 中行为的片段:

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

Output:

"{"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##

所以我在 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)
        }

    }
}

当使用自定义日期编码策略时,编码器拦截呼叫编码一个Date在给定的容器中,然后应用自定义策略.

然而与你的EncodableValue包装器,您没有给编码器执行此操作的机会,因为您直接调用底层值encode(to:)方法。和Date, this 将使用其默认表示形式对值进行编码,正如它的timeIntervalSinceReferenceDate.

要解决此问题,您需要在单个值容器中对基础值进行编码以触发任何自定义编码策略。这样做的唯一障碍是协议本身不符合,所以你不能调用容器的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"}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JSONEncoder 的 dateEncodingStrategy 不起作用 的相关文章

随机推荐

  • 简单的 SQL 查询给出 Invalid use of group function

    谁能告诉我为什么我会收到 Invalid use of group function 以及如何阻止它 SELECT Name Message FROM flux chat messages WHERE id gt MAX id 5 ORDE
  • 某个日期出现超过x次,获取下一个可用日期

    我有一个包含 15 列的数据框 其中 1 列是参与者 ID 14 列是每个参与者可能的预约日期 不包括节假日和周末 Included Participant V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V1
  • 如何添加带有多个标记的 Google 地图,在加载和单击时显示信息窗口

    我正在尝试添加带有多个标记的 Google 地图 所有标记都有信息窗口 我想显示所有信息窗口在页面加载时默认显示 当有人单击地图或标记时 所有信息窗口都必须关闭 然后通过单击标记来显示 这是我试图实现它的方法
  • ddd - 覆盖每个属性的值对象

    实施与从网站导出产品和导入订单的第三方系统的集成 设计中已经完成了一些 ddd 工作 我希望继续下去 订单和产品上的每个属性都应该包含一个值对象吗 这意味着大约 100 个类来涵盖所有可能的属性 这似乎过多 产品Sku 产品名称 产品描述
  • JQuery - 延迟对象数组的 $.when 语法[重复]

    这个问题在这里已经有答案了 这是我第一次使用 when我在语法上遇到困难 我的代码类似于下面的简化示例 它有效 如果我在简化时没有引起错误 我的问题是我不知道家里的很多元素customerIds数组将包含 var customerIds n
  • 错误1053:服务没有及时响应启动或控制请求

    我最近继承了几个作为 Windows 服务运行的应用程序 并且在为它们提供 GUI 可从系统托盘中的上下文菜单访问 时遇到问题 我们需要 Windows 服务的 GUI 的原因是为了能够重新配置 Windows 服务的行为 而无需停止 重新
  • MySQL 获取行,但更喜欢一个列值而不是另一列值

    有点奇怪 我想编写一个 MySQL 查询来从表中获取结果 但更喜欢列的一个值而不是另一个值 即 id name value prioirty 1 name1 value1 NULL 2 name1 value1 1 3 name2 valu
  • 在Google Guava(Java)中,如何批量设置ArrayTable的值?

    我有一个二维数据数组 例如 V 我想批量设置数组表实例 我必须反复打电话吗ArrayTable put R rowKey C columnKey V value 我找不到合适的构造函数 静态创建助手或方法 例如 putAll V value
  • 如何指示应首先编辑变量

    我有以下实时模板 import NAME from PATH END 当它插入编辑器时 输入变量的顺序定义为 import 1 from 2 有什么办法可以改成 import 2 from 1 只需使用编辑变量编辑该实时模板时按钮 然后使用
  • 在 2.2 上获取 ListView 项目的视图/逆序;适用于 4.0.3

    我有一个 ListView 它显示 ArrayAdapter 中的项目 我想在点击视图时为其设置动画 问题是在不同版本的 android 上我得到不同的视图 见下文 我使用此方法从 ListActivity 获取视图 private Vie
  • Java:如何使用UrlConnection发送授权请求?

    我想向需要身份验证的服务器生成 POST 请求 我尝试使用以下方法 private synchronized String CreateNewProductPOST String urlString String encodedString
  • 如何将 KB2670838 包含在 InstallShield 2013 的安装程序中?

    我正在使用 InstallShield 2013 为需要的应用程序制作基本 MSI 安装程序Windows 平台更新 KB2670838 对于 NET 框架和其他要求 我在 Redistributables 部分的 InstallShiel
  • 使用包含竖线的正则表达式匹配字符串

    I have 123 456 789 我只能捕捉 123 使用正则表达式 d 但不确定如何捕获完整的字符串 我对此很陌生 我将感谢任何帮助 d 这应该有效 从竖线开始 重复数字和可选的竖线
  • Graphviz---随机节点顺序和经过标签的边

    我有以下点文件 digraph finite state machine rank same node shape doublecircle q 5 node shape circle q 1 gt q 2 label q 1 gt q 2
  • 无法使用pip安装python包,需要获取Microsoft Visual C++ 14.0

    我正在尝试安装pyjks 我正在管理命令提示符下运行所有 内容 最初尝试安装 pyjks 的结果是 C WINDOWS system32 gt pip install pyjks Collecting pyjks Collecting py
  • 带标题的 PHP Mail() 函数

    当我将 header 与 PHP mail 函数一起使用时 我总是很困难 问题总是一样的 去年 这次 只要我记得 就让我抓狂 问题在于标题仅显示在电子邮件中 收到的邮件示例 Source onderwerp Bedankt voor uw
  • 防止 jQuery Mobile 上的水平滚动

    有没有办法最好仅使用 CSS 来防止移动设备上的水平页面滚动 这是一个例子 http jsfiddle net YfLst 15 Update 以下代码解决了 iOS 上的问题 但 Android 上的问题仍然存在 html body ov
  • 如何在 Eclipse 中以调试模式运行外部工具

    由于各种原因 我的项目只能作为已完成且打包的 JAR 运行 组装时会发生一些神奇的事情 因此我将其作为 Eclipse 中的外部工具运行 我缺少的是调试功能 有没有办法在 Eclipse 中以调试模式运行外部工具 如果远程 JVM 已在调试
  • 记录完成谷歌表单所需的时间

    我正在尝试记录完成并提交 Google 表单所需的总时间 我的逻辑很简单 以下代码将记录时间戳并将其作为多项选择选项 然后 在提交表单后 我们无论如何都会得到一个时间戳 但与此同时 我们也会得到最初记录的时间戳作为该问题的答案 这是我可爱的
  • JSONEncoder 的 dateEncodingStrategy 不起作用

    我正在尝试使用 Swift 4 的 Encodable JSONEncoder 将结构序列化为字符串 该对象可以保存异构值 例如 String Array Date Int 等 除了日期之外 所使用的方法工作正常 JSON编码器的dateE