考虑以下要点:https://gist.github.com/anonymous/0703a591f97fa9f6ad35b29234805fbd - 没有有效的方法来显示所有相关信息,因为文件相当长。道歉
我有一些嵌套的JSON对象,我试图解码。
{
"establishments":[
{ ... }
],
"meta":{ ... },
"links":[
]
}
^ establishments
数组包含Establishment
个对象
我的代码成功运行到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对象的输出是:summots wrong
- 在ViewController.swift
的第11行。
我认为它没有按预期工作的原因是因为JSON格式是这样的:
{
"establishments":[ ... ],
"meta":{ ... },
"links":[ ]
}
但我不知道我的班级结构/我在视图控制器中做的事情是否符合此布局标准。
有人能发现我出错的地方吗?我认为这是一个非常基本的东西,但我似乎无法理解它
我也明白,我不应该用大写字母命名类的属性 - 为违反任何惯例而道歉
编辑:我打印错误而不是我自己的声明,如下所示:
代替: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
时,我使用默认为XML格式的API。
我使用推荐的//print("test")
行
ViewController.swift
文件中的print(String(data: data, encoding: .utf8))
。
我使用Postman使用以下标头:x-api-version: 2
,accept: application/json
和content-type: application/json
来检索我的JSON输出。如果我在浏览器窗口中使用相同的GET字符串(所有参数都已正确填写),我将收到一条错误,指出:XML格式的The API 'Establishments' doesn't exist
。
我有什么方法可以使用此网址发送标题吗?
编辑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
标头值为2。
有什么想法吗?
编辑3:
addValue
的正确格式应该是:
request.addValue("2", forHTTPHeaderField: 'x-api-version
等等。
我现在面临着班级宣言的问题,特别是在我的“元”杂志中。类。
Meta.(CodingKeys in _DF4B170746CD5543281B14E0B0E7F6FB).dataSource], debugDescription: "Expected String value but found null instead.", underlyingError: nil
它抱怨,因为我的类声明与它对JSON对象期望的数据不匹配。
在我的JSON响应中(参考要点),有两个meta
个对象,一个包含null
的值dataSource
,另一个包含Lucene
。< / p>
我有什么方法可以接受&#39; null&#39;我的类初始化器中的值和字符串,同时保持符合Decodable协议?
JSON响应的那部分对我来说已经死了,我是否还需要为它创建对象,还是我可以忽略它们而逃脱? - 没有声明任何事情。或者我是否需要记下JSON响应中的所有内容才能使用数据?
答案 0 :(得分:1)
此代码应以Json的形式检索数据,并在标题中设置正确的内容类型。
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
的属性都需要声明为可选属性。仅考虑data-source
可以为null,您的元对象声明将如下所示:
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重新定义键。
答案 1 :(得分:1)
要自动解码,您的模型类属性必须与响应值类型匹配。从JSON响应中,我可以看到很少数据模型属性与JSON值类型不匹配。例如:
//inside response
"Hygiene": 10, // which is integer type
// properties in Scores
let Hygiene: String
您必须将模型类属性更改为响应或JSON响应作为类属性。
为了进行演示,我修改了您的模型类,并进行了以下更改:
// 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
使用以下更改替换您的模型类:
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
}
}
希望它能奏效。