如何正确地将复杂的 Swift 对象序列化/反序列化为 AWS Lambda 有效负载?

Posted

技术标签:

【中文标题】如何正确地将复杂的 Swift 对象序列化/反序列化为 AWS Lambda 有效负载?【英文标题】:How do I correctly serialize/deserialize a complex Swift object to as an AWS Lambda payload? 【发布时间】:2021-02-09 18:55:27 【问题描述】:

我的用例是存储绘图数据。我在 Swift 中定义了一个Codable 数据对象,如下所示:

class DrawingData : Codable 
  var startTime : Double?
  var pointsOverTime : [String : CGPoint] = [:]
  var templateLine : [CGPoint]

(事实证明,像CGPoint 这样的对象有一个非常好的Codable 实现,如下所示)。 在我的 Java 8 Lambda 函数中,我以这种方式定义了对象:

public class DrawingData 
  Double startTime;
  HashMap<String, Double[][]> pointsOverTime;
  Double[][] templateLine;

要在 ios 端进行编码,我很乐意这样做:

func sendToLambda(drawingData : DrawingData) 
  let request = AWSLambdaInvokerInvocationRequest()
  request.payload = drawingData
  ...

但在这种情况下,我得到一个NSInvalidArgumentException: Invalid type in JSON write。好的,没问题,我想,我会对对象进行编码:

let encoder = JSONEncoder()
let data = try encoder.encode(drawingData)
let json = String(data: data, encoding: .utf8)

起初,我尝试了request.payload = data,但这给了我与上面相同的NSInvalidArgumentException 错误。当我尝试request.payload = json 时,它实际上将其发送到 Lambda,但随后我收到来自 Lambda 的反序列化错误:

com.fasterxml.jackson.databind.JsonMappingException: 
Can not instantiate value of type [simple type, class com.(...).DrawingData] from String value ...

现在是真正让我感动的部分。我复制字符串值(来自上述错误),将转义引号转换为引号,并将确切的字符串作为测试对象粘贴到 Lambda 控制台中,它工作得很好! (我还尝试使用 .replacingOccurrences(of: "\\\"", with: "\"") 在我的代码中修改我的 json 字符串 - 没有骰子)。

因此,问题似乎在于 a) Swift JSONEncoder 和/或 String.init(data:),或者 b) 关于作为 AWS Lambda 环境一部分的 Jackson 配置的一些东西。我不确定如何确定它是哪一个,或者修复它的最佳方法是什么。

我可能会提交一个请求,让 AWS iOS 开发工具包接受 Codable 对象作为有效负载,但与此同时,有什么建议可以解决这个问题吗?我可以尝试任何替代方案吗?提前致谢!

编辑:按照建议,这是打印到我的 XCode 控制台的 JSON 的 sn-p:

\"startTime\":1612896264.1828671,
\"templateLine\":[[115.53672736136741,335.1095896791432],
[1055.5423897415162,193.17949493333694]],\"pointsOverTime\":
\"363899.94621276855\":[880.5,145.5],\"209530.83038330078\":
[242,347.5],\"331470.9663391113\":[843,156.5],\"175115.10848999023\":
[187,369.5],\"156777.85873413086\":[173,373],\"209379.9114227295\":
[242,347.5],\"281613.826751709\":[698,197.5],\"265787.12463378906\":
[606.5,224],\"347551.8226623535\":[868.5,148.5],\"174979.92515563965\":
[187,369.5],\"0\":[172.5,373],\"156646.9669342041\":
[173,373],\"224991.7984008789\":[331,313]...

【问题讨论】:

可以添加被拒绝的json吗?它可能会提供线索。 @flanker 我按照您的建议从 json 添加了一个 sn-p。正如我上面所说,将这些数据(在取消转义引号之后)复制并粘贴到 Lambda 控制台中的测试事件中就可以了。 【参考方案1】:

我在找到this 答案后解决了这个问题,这导致我在iOS端使用以下代码:

extension Encodable 
  func asDictionary() throws -> [String: Any] 
    let data = try JSONEncoder().encode(self)
    guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else 
      throw NSError()
    
    return dictionary
  

在我的Codable 对象上调用它后,我有一些可以分配为有效负载的东西:

let payloadObject = drawingData.asDictionary()
request.payload = payloadObject

然后通过将 那个 对象打印到 XCode 控制台,我能够准确地确定如何在 Java 中构造我的 Lambda 请求对象。

另一种可能有帮助的方法是在我的 Lambda 函数中实现 RequestStreamHandler(而不是 RequestHandler),然后使用 GSON 或类似的东西手动反序列化。但我真的很高兴能够利用Codable 协议和 POJO 作为模型。

【讨论】:

不错的解决方案。 Codable 的好处的问题是我们忘记了使用旧的 JSONSerialisation 可能发生的一切。

以上是关于如何正确地将复杂的 Swift 对象序列化/反序列化为 AWS Lambda 有效负载?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确地将 JSON 字符串反序列化为包含另一个类的嵌套列表的类

如何正确使用javascript反序列化将json字符串转换为复杂对象?

如何将 XML Choice 反序列化为正确的复杂类型

如何将XML Choice反序列化为正确的复杂类型

使用 Alamofire 和 Swift 3 反序列化复杂的 JSON

如何正确地将复杂属性 (ArrayList<POJO>) 设置为 GWT BaseTreeModel?序列化问题