如何从通过 Moya.Response 查询返回的对象解析嵌套的 JSON 数组

Posted

技术标签:

【中文标题】如何从通过 Moya.Response 查询返回的对象解析嵌套的 JSON 数组【英文标题】:How to parse nested JSON arrays from object returned via Moya.Response query 【发布时间】:2019-04-03 10:34:52 【问题描述】:

我正在尝试解析我的网络会话(通过 MoyaProvider 完成)产生的 JSON 对象。返回的 JSON 对象中包含嵌套的 JSON 数组。它看起来像这样:

编辑:指向 json 文件的链接在这里。 results.json


“resultCount” : 50,
“results” :
[
    
        “data1”: 1
        “data2”: 2
    ,
    
        “data1”: 1
        “data2”: 2
    ,
    
        “data1”: 1
        “data2”: 2
    
]

现在使用 Moya,我可以像这样使用 Moya.Response API 获取数据:

let jsonObj = try response.mapJSON()

但我不想这样做,我想将它映射到我的模型结构。我在下面做了类似的事情。我已经检查过(通过 OPTION + MouseHover)objMovie 是 [Movie] 类型的

let objMovie = try response.map(ITunesSearchResults<Movie>.self).results

我已经使用类似的技术在线学习了一个教程,但我会 不明白为什么 objMovie 不包含执行上述行后的返回值。我试着做一个

print(obj.< propertyofMovie >)

但控制台上没有显示任何内容。

那是什么?

这里有一些代码 sn-ps。 iTunesSearchResults 在哪里:

struct ITunesSearchResults<T: Decodable>: Decodable 
    let results: [T]

我的电影结构是这样的。它符合 JSON 嵌套数组属性中的键值。

struct Movie: Codable

   let trackId: Int
   let trackName: String
   let trackGenre: String
   let trackPrice: Int?
   let longDescription: String

   init(trackId: Int, trackName: String, trackGenre: String, 
   trackPrice: 
   Int?, /*trackImage: Thumbnail,*/ longDescription: String)
   
    self.trackId = trackId
    self.trackName = trackName
    self.trackGenre = trackGenre
    self.trackPrice = trackPrice ?? 0
    self.longDescription = longDescription
    //self.trackImage = trackImage //TODO: thumbnail: mapp url from json
   

  private enum MovieCodingKeys: String, CodingKey
  
    case trackId
    case trackName
    case trackGenre = "primaryGenreName"
    case trackPrice
    //case trackImage
    case longDescription
  

  init(from decoder: Decoder) throws
  
    let container = try decoder.container(keyedBy: MovieCodingKeys.self)

    trackId = try container.decode(Int.self, forKey: .trackId)
    trackName = try container.decode(String.self, forKey: .trackName)
    trackGenre = try container.decode(String.self, forKey: .trackGenre)
    trackPrice = try container.decode(Int?.self, forKey: .trackPrice)
    longDescription = try container.decode(String.self, forKey: .longDescription)
  

【问题讨论】:

尝试app.quicktype.io解析json 编辑后包含来自超链接的 results.json 文件 @Doc 这个工具很有帮助!但是,我在使用 JSONDecoder().decode() 方法时遇到问题,因为我使用 Moya 提供的 response.mapJSON() 方法将我的 jsonObj 设置为 Any 类型。我无法强制将 Any 类型的 jsonObj 强制转换为类型 Data,这是上述方法的第二个参数接受的数据类型。我得到的错误是“无法将 NSDictionary 类型的值转换为 NSString”。有正确的方法吗? 您应该使用response.data、github.com/Moya/Moya/blob/master/Sources/Moya/Response.swift 属性得到Data 类型的响应,您能否确认 response.data 不可用? 【参考方案1】:

首先使用MoyaData 响应,因为您确实想用Decodable 解码JSON

let data = response.data

您的Movie 结构太复杂,显式CodingKeysinit 方法根本不需要,您可以免费获得它们。这就足够了:

struct ITunesSearchResults<T: Decodable>: Decodable 
    let results: [T]


struct Movie: Decodable

    let trackId: Int
    let trackName: String
    let trackGenre: String?
    let trackPrice: Double?
    let longDescription: String

注意trackPriceDoubletrackGenretrackPrice 是可选的。

现在简单解码

do 
    let result = try JSONDecoder().decode(ITunesSearchResults<Movie>.self, from: data)
    print(result)
 catch  print(error) 

注意:

从不使用像

这样的语法
try container.decode(Int?.self, forKey: .trackPrice)

decodeIfPresent

try container.decodeIfPresent(Int.self, forKey: .trackPrice)

最大的好处是,如果键存在但类型错误,就像在这个具体案例中一样,它会引发错误。

【讨论】:

但没有实现将正确的 JSON 键值映射到我的结构所需的 CodingKey。例如,trackGenre 在 JSON 对象中的名称不同。这是primareGenreName 您上面的代码实际上已经打印了一些数据,这意味着这个result 对象不为NULL。但是,为什么不需要将 .results 附加到解码器行?因为我正在寻找一个[Movie] 对象,这将返回一个ITunesSearchResults&lt;Movie&gt;.self 对象(但至少这个不是空的!) 好的,如果你想重命名键,你必须指定 CodingKeys(但不是 init 方法)。你得到Movie 数组和result.results 谢谢。注释很有帮助。但是我仍然对何时应该实施/使用init(from decoder: Decoder) throws 感到困惑?任何示例用例? init(from 仅应在您需要 custom 行为时实施,例如为同一个键考虑两种不同类型。简单地说:如果 JSON 可以使用合成的 init 方法正确解码,请不要提供自己的。

以上是关于如何从通过 Moya.Response 查询返回的对象解析嵌套的 JSON 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何向 Moya.Response JSON 添加一个字段,该字段不在来自 http 响应的真实有效负载中

VBA 开发从数据库中查询数据,怎样返回一个数组?

如何从 Dapper 查询返回 null 而不是 default(T)?

如何通过异步调用 uid 从 Firebase 查询数据

查询不会通过 GraphQL 从 DynamoDB 返回某些项目

如何在PHP中获取MYSQL数据库返回的数据的行数?