Decoding JSON object (发送带参数的GET请求) - Swift 4 Decodable
Decoding JSON object (sending GET request with parameters) - Swift 4 Decodable
考虑以下要点:https://gist.github.com/anonymous/0703a591f97fa9f6ad35b29234805fbd - 没有有效的方法来显示所有相关信息,因为文件相当长。道歉
我有一些嵌套的 JSON object 正在尝试解码。
{
"establishments":[
{ ... }
],
"meta":{ ... },
"links":[
]
}
^ establishments
数组包含 Establishment
objects
我的代码成功运行到 ViewController.swift
的第 7 行,在那里我实际使用了 Swift 4 的 JSONDecode()
功能。
ViewController.swift
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
//print("test")
do {
let establishments = try JSONDecoder().decode(Establishments.self, from: data)
print(establishments)
} catch {
print("summots wrong")
}
}).resume()
我在 Establishment.swift
中看到的映射 JSON object 的输出是:summots wrong
- 在 ViewController.swift
的第 11 行。
我认为它没有按预期工作的原因是因为 JSON 格式是这样的:
{
"establishments":[ ... ],
"meta":{ ... },
"links":[ ]
}
但我不知道我的 class 结构/我在视图控制器中所做的是否满足此布局标准。
谁能发现我哪里出错了?我认为这是非常基本的东西,但我似乎无法理解它
我也明白我不应该用大写字母命名 classes 的属性 - 对于违反任何约定表示歉意
编辑:我打印了错误而不是我自己的声明,如下所示:
而不是:print("summots wrong")
我写了:print(error)
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
正如 Hamish 正确指出的那样,我没有从 GET 请求中收到正确的 JSON 数据。事实上,我根本没有收到任何 JSON。当 x-api-version
未设置为 2
时,我使用的 API 默认为 XML 格式。
我的 ViewController.swift
文件中没有使用 //print("test")
,而是使用了推荐的:print(String(data: data, encoding: .utf8))
行
我将 Postman 与以下 header 一起使用:x-api-version: 2
、accept: application/json
和 content-type: application/json
来检索我的 JSON 输出。如果我在浏览器 window 中使用相同的 GET 字符串(所有参数均已正确填写),我将收到一条错误消息:The API 'Establishments' doesn't exist
格式为 XML。
有什么方法可以让我用这个 URL 发送一个 header 吗?
编辑 2:我已将请求更改为使用 URLRequest
而不仅仅是 URL
。
这是我更新的 ViewController.swift
代码:
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("x-api-version", forHTTPHeaderField: "2")
request.addValue("accept", forHTTPHeaderField: "application/json")
request.addValue("content-type", forHTTPHeaderField: "application/json")
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
print(String(data: data, encoding: .utf8))
// do {
// let establishments = try JSONDecoder().decode(Establishments.self, from: data)
// print(establishments)
// } catch {
// print(error)
// }
}).resume()
我收到错误:
HTTP Error 400. The request has an invalid header name
我做了一些检查,请求只是忽略了 x-api-version
header 值 2。
有什么想法吗?
编辑 3:
addValue
的正确格式应该是:
request.addValue("2", forHTTPHeaderField: 'x-api-version
等等。
我现在面临 class 声明的问题,特别是在我的 'meta' class.
中
Meta.(CodingKeys in _DF4B170746CD5543281B14E0B0E7F6FB).dataSource], debugDescription: "Expected String value but found null instead.", underlyingError: nil
它在抱怨,因为我的 class 声明与它期望的来自 JSON object 的数据不匹配。
在我的 JSON 回复中(参考要点)有两个 meta
object,一个包含 null
的值 dataSource
另一个是 Lucene
.
有没有什么方法可以让我在我的 class 初始化程序中接受 'null' 值和字符串,同时遵守 Decodable 协议?
JSON 响应的那部分对我来说已经死了,我是否仍需要为其创建 objects,或者我可以忽略它们吗? - 无需声明任何内容。或者我是否特别需要记下 JSON 响应中的所有内容才能使用数据?
此代码应将数据检索为 Json,并在 headers 中设置正确的 content-type。
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("2", forHTTPHeaderField: "x-api-version")
request.addValue("application/json", forHTTPHeaderField: "accept")
request.addValue("application/json", forHTTPHeaderField: "content-type")
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
do {
let establishments = try JSONDecoder().decode(Establishments.self, from: data)
print(establishments)
} catch {
print(error)
}
}).resume()
那么每一个可以是null
的属性都需要声明为optional。考虑到只有 data-source
可以为 null,您的元 object 声明将是这样的:
class Meta: Decodable {
let dataSource: String?
let extractDate: String
let itemCount: Int
let returncode: String
let totalCount: Int
let totalPages: Int
let pageSize: Int
let pageNumber: Int
}
最后一点,如果您希望在模型中的所有地方都使用小写属性,您可以通过 overriding the CodingKey enum 重新定义键。
要自动解码,您的模型 class 属性必须与响应值类型相匹配。从 JSON 响应中,我可以看到很少有数据模型属性与 JSON 值类型不匹配。例如:
//inside response
"Hygiene": 10, // which is integer type
// properties in Scores
let Hygiene: String
要么您必须将模型 class 属性更改为响应,要么将 JSON 响应更改为您的 class 属性。
为了演示,我修改了您的模型 class 并进行了以下更改:
// changed from String
let Hygiene: Int
let Structural: Int
let ConfidenceInManagement: Int
// changed from Double
let longitude: String
let latitude: String
// changed to optional
let dataSource: String?
let returncode: String?
// changed from Int
let RatingValue: String
用以下更改替换您的模型 classes:
class Scores: Decodable {
// set as string because we can expect values such as 'exempt'
let Hygiene: Int
let Structural: Int
let ConfidenceInManagement: Int
init(Hygiene: Int, Structural: Int, ConfidenceInManagement: Int) {
self.Hygiene = Hygiene
self.Structural = Structural
self.ConfidenceInManagement = ConfidenceInManagement
}
}
class Geocode: Decodable {
let longitude: String
let latitude: String
init(longitude: String, latitude: String) {
self.longitude = longitude
self.latitude = latitude
}
}
class Meta: Decodable {
let dataSource: String?
let extractDate: String
let itemCount: Int
let returncode: String?
let totalCount: Int
let totalPages: Int
let pageSize: Int
let pageNumber: Int
init(dataSource: String, extractDate: String, itemCount: Int, returncode: String, totalCount: Int, totalPages: Int, pageSize: Int, pageNumber: Int) {
self.dataSource = dataSource
self.extractDate = extractDate
self.itemCount = itemCount
self.returncode = returncode
self.totalCount = totalCount
self.totalPages = totalPages
self.pageSize = pageSize
self.pageNumber = pageNumber
}
}
class Establishments: Decodable {
let establishments: [Establishment]
init(establishments: [Establishment]) {
self.establishments = establishments
}
}
class Establishment: Decodable {
let FHRSID: Int
let LocalAuthorityBusinessID: String
let BusinessName: String
let BusinessType: String
let BusinessTypeID: Int
let AddressLine1: String
let AddressLine2: String
let AddressLine3: String
let AddressLine4: String
let PostCode: String
let Phone: String
let RatingValue: String
let RatingKey: String
let RatingDate: String
let LocalAuthorityCode: Int
let LocalAuthorityName: String
let LocalAuthorityWebSite: String
let LocalAuthorityEmailAddress: String
let scores: Scores
let SchemeType: String
let geocode: Geocode
let RightToReply: String
let Distance: Double
let NewRatingPending: Bool
let meta: Meta
let links: [String]
init(FHRSID: Int, LocalAuthorityBusinessID: String, BusinessName: String, BusinessType: String, BusinessTypeID: Int, AddressLine1: String, AddressLine2: String, AddressLine3: String, AddressLine4: String, PostCode: String, Phone: String, RatingValue: String, RatingKey: String, RatingDate: String, LocalAuthorityCode: Int, LocalAuthorityName: String, LocalAuthorityWebSite: String, LocalAuthorityEmailAddress: String, scores: Scores, SchemeType: String, geocode: Geocode, RightToReply: String, Distance: Double, NewRatingPending: Bool, meta: Meta, links: [String]) {
self.FHRSID = FHRSID
self.LocalAuthorityBusinessID = LocalAuthorityBusinessID
self.BusinessName = BusinessName
self.BusinessType = BusinessType
self.BusinessTypeID = BusinessTypeID
self.AddressLine1 = AddressLine1
self.AddressLine2 = AddressLine2
self.AddressLine3 = AddressLine3
self.AddressLine4 = AddressLine4
self.PostCode = PostCode
self.Phone = Phone
self.RatingValue = RatingValue
self.RatingKey = RatingKey
self.RatingDate = RatingDate
self.LocalAuthorityCode = LocalAuthorityCode
self.LocalAuthorityName = LocalAuthorityName
self.LocalAuthorityWebSite = LocalAuthorityWebSite
self.LocalAuthorityEmailAddress = LocalAuthorityEmailAddress
self.scores = scores
self.SchemeType = SchemeType
self.geocode = geocode
self.RightToReply = RightToReply
self.Distance = Distance
self.NewRatingPending = NewRatingPending
self.meta = meta
self.links = links
}
}
希望它能奏效。
考虑以下要点:https://gist.github.com/anonymous/0703a591f97fa9f6ad35b29234805fbd - 没有有效的方法来显示所有相关信息,因为文件相当长。道歉
我有一些嵌套的 JSON object 正在尝试解码。
{
"establishments":[
{ ... }
],
"meta":{ ... },
"links":[
]
}
^ establishments
数组包含 Establishment
objects
我的代码成功运行到 ViewController.swift
的第 7 行,在那里我实际使用了 Swift 4 的 JSONDecode()
功能。
ViewController.swift
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
//print("test")
do {
let establishments = try JSONDecoder().decode(Establishments.self, from: data)
print(establishments)
} catch {
print("summots wrong")
}
}).resume()
我在 Establishment.swift
中看到的映射 JSON object 的输出是:summots wrong
- 在 ViewController.swift
的第 11 行。
我认为它没有按预期工作的原因是因为 JSON 格式是这样的:
{
"establishments":[ ... ],
"meta":{ ... },
"links":[ ]
}
但我不知道我的 class 结构/我在视图控制器中所做的是否满足此布局标准。
谁能发现我哪里出错了?我认为这是非常基本的东西,但我似乎无法理解它
我也明白我不应该用大写字母命名 classes 的属性 - 对于违反任何约定表示歉意
编辑:我打印了错误而不是我自己的声明,如下所示:
而不是:print("summots wrong")
我写了:print(error)
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
正如 Hamish 正确指出的那样,我没有从 GET 请求中收到正确的 JSON 数据。事实上,我根本没有收到任何 JSON。当 x-api-version
未设置为 2
时,我使用的 API 默认为 XML 格式。
我的 ViewController.swift
文件中没有使用 //print("test")
,而是使用了推荐的:print(String(data: data, encoding: .utf8))
行
我将 Postman 与以下 header 一起使用:x-api-version: 2
、accept: application/json
和 content-type: application/json
来检索我的 JSON 输出。如果我在浏览器 window 中使用相同的 GET 字符串(所有参数均已正确填写),我将收到一条错误消息:The API 'Establishments' doesn't exist
格式为 XML。
有什么方法可以让我用这个 URL 发送一个 header 吗?
编辑 2:我已将请求更改为使用 URLRequest
而不仅仅是 URL
。
这是我更新的 ViewController.swift
代码:
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("x-api-version", forHTTPHeaderField: "2")
request.addValue("accept", forHTTPHeaderField: "application/json")
request.addValue("content-type", forHTTPHeaderField: "application/json")
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
print(String(data: data, encoding: .utf8))
// do {
// let establishments = try JSONDecoder().decode(Establishments.self, from: data)
// print(establishments)
// } catch {
// print(error)
// }
}).resume()
我收到错误:
HTTP Error 400. The request has an invalid header name
我做了一些检查,请求只是忽略了 x-api-version
header 值 2。
有什么想法吗?
编辑 3:
addValue
的正确格式应该是:
request.addValue("2", forHTTPHeaderField: 'x-api-version
等等。
我现在面临 class 声明的问题,特别是在我的 'meta' class.
中Meta.(CodingKeys in _DF4B170746CD5543281B14E0B0E7F6FB).dataSource], debugDescription: "Expected String value but found null instead.", underlyingError: nil
它在抱怨,因为我的 class 声明与它期望的来自 JSON object 的数据不匹配。
在我的 JSON 回复中(参考要点)有两个 meta
object,一个包含 null
的值 dataSource
另一个是 Lucene
.
有没有什么方法可以让我在我的 class 初始化程序中接受 'null' 值和字符串,同时遵守 Decodable 协议?
JSON 响应的那部分对我来说已经死了,我是否仍需要为其创建 objects,或者我可以忽略它们吗? - 无需声明任何内容。或者我是否特别需要记下 JSON 响应中的所有内容才能使用数据?
此代码应将数据检索为 Json,并在 headers 中设置正确的 content-type。
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("2", forHTTPHeaderField: "x-api-version")
request.addValue("application/json", forHTTPHeaderField: "accept")
request.addValue("application/json", forHTTPHeaderField: "content-type")
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
do {
let establishments = try JSONDecoder().decode(Establishments.self, from: data)
print(establishments)
} catch {
print(error)
}
}).resume()
那么每一个可以是null
的属性都需要声明为optional。考虑到只有 data-source
可以为 null,您的元 object 声明将是这样的:
class Meta: Decodable {
let dataSource: String?
let extractDate: String
let itemCount: Int
let returncode: String
let totalCount: Int
let totalPages: Int
let pageSize: Int
let pageNumber: Int
}
最后一点,如果您希望在模型中的所有地方都使用小写属性,您可以通过 overriding the CodingKey enum 重新定义键。
要自动解码,您的模型 class 属性必须与响应值类型相匹配。从 JSON 响应中,我可以看到很少有数据模型属性与 JSON 值类型不匹配。例如:
//inside response
"Hygiene": 10, // which is integer type
// properties in Scores
let Hygiene: String
要么您必须将模型 class 属性更改为响应,要么将 JSON 响应更改为您的 class 属性。
为了演示,我修改了您的模型 class 并进行了以下更改:
// changed from String
let Hygiene: Int
let Structural: Int
let ConfidenceInManagement: Int
// changed from Double
let longitude: String
let latitude: String
// changed to optional
let dataSource: String?
let returncode: String?
// changed from Int
let RatingValue: String
用以下更改替换您的模型 classes:
class Scores: Decodable {
// set as string because we can expect values such as 'exempt'
let Hygiene: Int
let Structural: Int
let ConfidenceInManagement: Int
init(Hygiene: Int, Structural: Int, ConfidenceInManagement: Int) {
self.Hygiene = Hygiene
self.Structural = Structural
self.ConfidenceInManagement = ConfidenceInManagement
}
}
class Geocode: Decodable {
let longitude: String
let latitude: String
init(longitude: String, latitude: String) {
self.longitude = longitude
self.latitude = latitude
}
}
class Meta: Decodable {
let dataSource: String?
let extractDate: String
let itemCount: Int
let returncode: String?
let totalCount: Int
let totalPages: Int
let pageSize: Int
let pageNumber: Int
init(dataSource: String, extractDate: String, itemCount: Int, returncode: String, totalCount: Int, totalPages: Int, pageSize: Int, pageNumber: Int) {
self.dataSource = dataSource
self.extractDate = extractDate
self.itemCount = itemCount
self.returncode = returncode
self.totalCount = totalCount
self.totalPages = totalPages
self.pageSize = pageSize
self.pageNumber = pageNumber
}
}
class Establishments: Decodable {
let establishments: [Establishment]
init(establishments: [Establishment]) {
self.establishments = establishments
}
}
class Establishment: Decodable {
let FHRSID: Int
let LocalAuthorityBusinessID: String
let BusinessName: String
let BusinessType: String
let BusinessTypeID: Int
let AddressLine1: String
let AddressLine2: String
let AddressLine3: String
let AddressLine4: String
let PostCode: String
let Phone: String
let RatingValue: String
let RatingKey: String
let RatingDate: String
let LocalAuthorityCode: Int
let LocalAuthorityName: String
let LocalAuthorityWebSite: String
let LocalAuthorityEmailAddress: String
let scores: Scores
let SchemeType: String
let geocode: Geocode
let RightToReply: String
let Distance: Double
let NewRatingPending: Bool
let meta: Meta
let links: [String]
init(FHRSID: Int, LocalAuthorityBusinessID: String, BusinessName: String, BusinessType: String, BusinessTypeID: Int, AddressLine1: String, AddressLine2: String, AddressLine3: String, AddressLine4: String, PostCode: String, Phone: String, RatingValue: String, RatingKey: String, RatingDate: String, LocalAuthorityCode: Int, LocalAuthorityName: String, LocalAuthorityWebSite: String, LocalAuthorityEmailAddress: String, scores: Scores, SchemeType: String, geocode: Geocode, RightToReply: String, Distance: Double, NewRatingPending: Bool, meta: Meta, links: [String]) {
self.FHRSID = FHRSID
self.LocalAuthorityBusinessID = LocalAuthorityBusinessID
self.BusinessName = BusinessName
self.BusinessType = BusinessType
self.BusinessTypeID = BusinessTypeID
self.AddressLine1 = AddressLine1
self.AddressLine2 = AddressLine2
self.AddressLine3 = AddressLine3
self.AddressLine4 = AddressLine4
self.PostCode = PostCode
self.Phone = Phone
self.RatingValue = RatingValue
self.RatingKey = RatingKey
self.RatingDate = RatingDate
self.LocalAuthorityCode = LocalAuthorityCode
self.LocalAuthorityName = LocalAuthorityName
self.LocalAuthorityWebSite = LocalAuthorityWebSite
self.LocalAuthorityEmailAddress = LocalAuthorityEmailAddress
self.scores = scores
self.SchemeType = SchemeType
self.geocode = geocode
self.RightToReply = RightToReply
self.Distance = Distance
self.NewRatingPending = NewRatingPending
self.meta = meta
self.links = links
}
}
希望它能奏效。