将userInfo [AnyHashable: Any]转换为[String: Any]

7

我在didreceiveRemoteNotification中收到了通知,但是我无法将userInfo转换为类型为[String: Any]的字典

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    let dict = userInfo as! [String: Any]
    if let response = dict["message"] as? [String: Any], let baseResponse = Mapper<NotificationModel>().map(JSON: response) {
      //do some stuff
    }
}

当我尝试将dict["message"]强制转换为 [String: Any] 时,出现错误并提示:

无法将类型 '__NSCFString' (0x1cfa84f90) 强制转换为 'NSDictionary' (0x1cfa85bc0)。

这是在控制台中打印 dict["message"] 时的输出结果:

Optional<Any>
  - some : {"sender":

{"avatar_url":"http:\/\/api.moneyar.com\/APIs\/images\/15783070400.jpg","user_id":"15783","name":"mahdi moqadasi"}

,"conversation_id":"15783"

,"message_id":103597,

"time":1546778745,

"type":1,"message":"foo"

}

根据错误信息看起来 dict["message"] 是一个字符串,一个 JSON 字符串。所以应该使用 if let jsonStringified = dict["message"] as? String。我不使用 Mapper,而是使用标准工具:伪代码中的 let jsonData = jsonStringified.data(encoding: .utf8); let jsonDict = try? JSONSerialization.jsonObject(data: jsonData) - Larme
@Larme,我会尝试你的代码。 - andesta.erfan
@Larme 请将您的评论发布为答案。那是正确的。谢谢您纠正我。 - andesta.erfan
Mapper<NotificationModel>().map(JSON: response)Mapper是否有处理(NS)Data(NS)String JSON的方法?因为这样可以避免转换。我希望对于一个更高级别的第三方库来说是这样的。是这种情况吗?例如Mapper<NotificationModel>().map(JSONString: jsonStringified)Mapper<NotificationModel>().map(JSONData: jsonData),你使用的是这个吗:https://github.com/lyft/mapper/blob/master/Sources/Mapper.swift? - Larme
@Larme 是的。我只是将 jsonDict 转换为 [String: Any],然后将其传递给 Mapper,它成功地给了我一个模型。 - andesta.erfan
2个回答

4
对于以下答案,代码没有经过编译器测试,可能会有一些打字错误,但这些错误可以很容易地修复。其中一些是故意为之的,以突出其背后的逻辑,并且不添加if let/guard letas?等噪音解释。
我不会重复@vadian answer,因为他的回答是正确的并解释了为什么它失败了。
所以我们清楚了dict["message"]是一个String
你似乎在JSON缩写中缺少一条信息,即"N"代表什么:"Notation"。
当你打印dict["message"]时,你并没有真正得到一个键/值对象,而是得到一个表示键值对象的字符串,但不是用Swift表示的。你打印了JSON字符串(因为它显然比十六进制数据JSON更可读)。如果在回答后打印jsonDict,你会发现输出结构可能会有所不同。
所以,像往常一样,你的基本工具是:
Data <== data(encoding:)/init(data:encoding:) ==> String
Data <== jsonObject(with:options:)/data(withJSONObject:options:) ==> Array or Dictionary //I bypass voluntarily the specific case of String at top level

那我们就开始吧!

let jsonStringifiedString = dict["message"] as String
let jsonStringifiedData = jsonStringifiedString.data(using: .utf8) as Data
let jsonDict = try JSONSerialization.jsonObject(with: jsonStringifiedData, options: []) as [String: Any]
let baseResponse = Mapper<NotificationModel>().map(JSON: jsonDict)

如果我是你,我会查看 Mapper,如果没有像这样的方法:
let baseResponse = Mapper<NotificationModel>().map(JSONData: jsonStringifiedData)

或者

let baseResponse = Mapper<NotificationModel>().map(JSONString: jsonStringifiedString)

因为有时候JSON中会嵌套JSON字符串,需要直接在StringData上调用它。或者仅仅是基本的URLSession请求在其闭包中返回一个Data对象,你想直接使用它。


1

这个错误

无法将类型为 '__NSCFString' (0x1cfa84f90) 的值强制转换为类型为 'NSDictionary' (0x1cfa85bc0)。

很明显。键 message 的值是一个字符串

  • 类型 是实际的 类型
  • 应该是 预期的 错误 类型

if let response = dict["message"] as? String, ...

那不是一个答案。我打印dict["message"],一切都是键值对。 - andesta.erfan
@andesta.erfan,你看到的是JSON格式,这不是Swift字典的打印方式,所以它现在不是“键值对”,而只是一个JSON字符串。 - Larme
该值肯定是一个字符串。看起来这个字符串是JSON格式的,你需要单独反序列化它(也就是使用Mapper对象进行操作)。 - vadian
好的,但是你说的“它只是一个JSON字符串”是什么意思?我该如何将其转换为[String: Any]类型? - andesta.erfan
1
请阅读我在你的问题上的评论。JSON只是一种表示方式。JSON中的“N”代表“符号”。这通常是新开发人员忽略的一个步骤。数据表示,字符串表示,最后是序列化后在您自己的语言表示中的字典/数组。因此,当某些JSON中嵌入了JSON字符串时,就像在您的情况下一样,解决它的方法总是相同的。 - Larme

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接