Swift5:如何将此 JSON 响应处理到 Swift 数据模型中

Swift5 : How to handle this JSON response into Swift Data Model

提问人:Ajmal Orawala 提问时间:3/17/2022 最后编辑:Ajmal Orawala 更新时间:3/17/2022 访问量:2015

问:

我是 Swift 的新手,所以如果这是一个愚蠢的问题,请原谅我 如何将这类响应处理成数据模型,响应是这样的:

{
    "id" = 1;
    count = "";
    data = "[{"key":"value","key":"value".......}]";
    message = SUCCESS;
    "response_code" = 1;
}

AlamofireRequest 获取响应并打印它,但是当使用 responseDecodable 时,没有任何反应,Alamofire 请求的代码如下:

let request = AF.request(urlString!,method: .get)
        
request.responseJSON { (data) in
  print(data)
}
        
request.responseDecodable(of: Test.self) { (response) in 
    guard let getData = response.value else {return}
    print(getData.all[0].firstName)  //Nothing prints
}

这就是数据模型的样子:

struct Test: Decodable {
   
    let firstName: String

    enum CodingKeys: String, CodingKey {
        case firstName = "first_name" 
        //firstname is inside data of json
    }
}

struct Getdata: Decodable {
    let all : [Test]
    
    enum CodingKeys: String, CodingKey {
        case all = "data"
    }
}

想要访问数据中的值并打印它。请阐明一下!

iOS JSON Swift 解析 AlamoFire

评论

0赞 vadian 3/17/2022
key 的值是二级 JSON 。它不能直接解码到模型中。dataString
0赞 Larme 3/17/2022
我不知道是什么,但不是直接检查,而是您可能想使用 to 属性处理 ,查看解析响应时是否存在错误等。Reservationsresponse.valueswitchResult
0赞 flanker 3/17/2022
这里似乎有很多问题,包括上述问题。对于初学者来说,响应不是 JSON,您需要解码包装器类型 - 然后在其中访问,而不是直接解码。此外,该类型与 json 中的数据几乎没有相似之处,尤其是因为没有键。Getdata.selfallTest.selfTestfirst_name
0赞 Ajmal Orawala 3/17/2022
我已经编辑了问题,键“first_name”是内部数据。Getdata 结构是解包“数据”。Test 包含变量 (firstname) 来存储从 JSON 响应的“data”属性获取的值。我已经更改了命名,请耐心等待。我目前的目标是实现功能,如果不遵循准则或命名约定,请原谅。

答:

0赞 flanker 3/17/2022 #1

试图解决上面的问题和评论,第一件事是获取一些有效的 JSON。可能是您的 API 正在提供非 JSON 数据,在这种情况下,此答案或使用 AlamoFire 对其进行解码都不起作用。但是,将上述格式化为 JSON,以我对它含义的最佳猜测,将给出:

let json = """
{
  "id": 1,
  "count": "",
  "data": [
    {
      "key1": "value1",
      "key2": "value2"
    }
  ],
  "message": "SUCCESS",
  "response_code": 1
}
"""

在这一点上,更明显的是,键下的内容不是上面定义的数组,而是一个字典数组(在示例中,数组中只有一个 enry)。因此,有必要重新指定数据模型。对于这么简单的事情,对于上面提到的用例,没有真正的理由选择多种类型,所以我们将重新定义 Getdata(尽管我不喜欢它,但我坚持使用您的命名):dataTest

struct Getdata: Decodable {
   let all : [[String:String]]
   
   enum CodingKeys: String, CodingKey {
      case all = "data"
   }
}

为了测试解码,让我们使用标准的 JSONDecoder(我手边没有 AlamoFire 的 Playground,因为我永远不会使用它):


do {
   let wrapper = try JSONDecoder().decode(Getdata.self, from: Data(json.utf8))
   print(wrapper.all)
} catch {
   print("Decoding error \(error.localizedDescription)")
}

这输出

[[“key1”: “值 1”, “key2”: “值 2”]]

正如预期的那样。

现在有一个有效的JSON解码解决方案,如果你愿意,应该很容易将其放入AlamoFire API中。尽管如果后端确实提供了格式错误的 JSON,这是行不通的,您必须更改后端,使用自己的解码器对其进行解码或将其修改为有效的 JSON。

评论

0赞 Ajmal Orawala 3/17/2022
您的重新指定模型的解决方案非常完美,访问[[“key1”:“value1”,“key2”:“value2”]]中的密钥的有效方法应该是什么
0赞 flanker 3/17/2022
这是一个简单的字典,所以你可以像往常一样访问它 egwrapper.all[“key1”] 来获取值。根据你想如何使用它,将它分配为持久化作为局部变量可能会有所帮助 let myDictionary = wrapper.all
0赞 Ajmal Orawala 3/17/2022 #2

在原来的问题中犯了很多愚蠢的错误,还有很长的路要走

正如@flanker所建议的那样,我确实将数据模型重新指定为

struct Test : Codable {
    let id : String?
    let message : String?
    let data : String?
    let count : String?
    let response_code : String?

    enum CodingKeys: String, CodingKey {

        case id = "$id"
        case message = "message"
        case data = "data"
        case count = "count"
        case response_code = "response_code"
    }
       init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decodeIfPresent(String.self, forKey: .id)
        message = try values.decodeIfPresent(String.self, forKey:  .message)
        data = try values.decodeIfPresent(String.self, forKey: .data)
        count = try values.decodeIfPresent(String.self, forKey: .count)
        response_code = try values.decodeIfPresent(String.self, forKey: .response_code)
    }

现在它似乎起作用了

现在获得所需的输出[["key1": "value1", "key2": "value2"]]