【问题标题】:Encoding an array of generic structs对泛型结构数组进行编码
【发布时间】:2021-03-18 18:19:07
【问题描述】:

我正在尝试进行 API 调用,该调用采用 JSON 请求正文,如下所示:

[
  { "op": "replace", "path": "/info/name", "value": "TestName" },
  { "op": "replace", "path": "/info/number", "value": 100 },
  { "op": "replace", "path": "/info/location", "value": ["STATE", "CITY"] },
  { "op": "replace", "path": "/privacy/showLocation", "value": true }
]

我有一些 oppath 值的枚举:

enum ChangeOp: String, Encodable {
  case replace
  case append
}

enum ChangePath: String, Encodable {
  case name = "/info/name"
  case number = "/info/number"
  case location = "/info/location"
  case showLocation = "/privacy/showLocation"
}

this answer,我发现你必须使用协议来创建通用结构数组,所以我有以下协议和结构:

protocol UserChangeProto {
  var op: ChangeOp { get }
  var path: ChangePath { get }
}

struct UserChange<ValueType: Encodable>: Encodable, UserChangeProto {
  let op: ChangeOp
  let path: ChangePath
  let value: ValueType
}

这里是编码发生的地方:

func encodeChanges(arr: [UserChangeProto]) -> String? {
  let encoder = JSONEncoder()
  guard let jsonData = try? encoder.encode(arr) else {
    return nil
  }
  return String(data: jsonData, encoding: String.Encoding.utf8)
}

func requestUserChanges(changes: String) {
  print(changes)

  // make API request ...
}

requestUserChanges(changes:
  encodeChanges(arr: [
    UserChange(op: .replace, path: .name, value: "TestName"),
    UserChange(op: .replace, path: .number, value: 100),
    UserChange(op: .replace, path: .location, value: ["STATE", "CITY"]),
    UserChange(op: .replace, path: .showLocation, value: true)
  ]) ?? "null"
)

问题是当我尝试运行encoder.encode(arr) 时,我收到以下错误:Value of protocol type 'UserChangeProto' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols

我的问题是,我怎样才能绕过这个错误?或者换句话说,编码泛型结构数组的最简单方法是什么

编辑:所以看起来这是 Swift 语言本身的问题,Swift team is looking into.我不确定如何在此处继续...

【问题讨论】:

    标签: swift generics jsonencoder


    【解决方案1】:

    您可能会发现类型擦除编码很有用:https://github.com/Flight-School/AnyCodable

    使用上面的 AnyEncodable:

    struct Change<V: Encodable>: Encodable {
        enum Op: String, Encodable {
            case replace
            case append
        }
        
        enum Path: String, Encodable {
            case name = "/info/name"
            case number = "/info/number"
            case location = "/info/location"
            case showLocation = "/privacy/showLocation"
        }
        
        var op: Op
        var path: Path
        var value: V
    }
    
    let encoder = JSONEncoder()
    let changes: [Change<AnyEncodable>] = [
        Change(op: .append, path: .name, value: "Foo"),
        Change(op: .replace, path: .number, value: 42)
    ]
    
    let r = try? encoder.encode(changes)
    
    String(data: r!, encoding: .utf8)
    

    满足您的期望

    【讨论】:

    • 不幸的是,我遇到了一些阻力,因为我只为这个用例添加了一个新的依赖项。不过看了一下代码,他们似乎只是手动检查了所有符合 Codable 的类型。我最终为结构写了类似的东西:gist.github.com/rgajrawala/f3d009b2505f67f8ca3c28f317f03f0b。如果没有回击,我只会使用 AnyCodable,所以我正在标记您的答案。谢谢!
    • 原来你也可以使用关联值枚举,这比每个类型都有一个成员的结构更优雅。我已经更新了要点以表明这一点。您还可以在枚举中使用计算属性来固定键和值类型,这样开发人员就不会意外使用错误的类型。枚举比我最初想象的要强大得多,必须再看看它们!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-13
    • 1970-01-01
    • 2019-10-24
    • 1970-01-01
    相关资源
    最近更新 更多