[英]JSONDecoder returning nil while parsing
I have the following function:我有以下 function:
func executeGet( completion: @escaping (Data?, Error?) -> Void) {
AF.request("https:URL",
method:.get,
headers:headers).response{ response in
debugPrint(response)
if let error = response.error {
completion(nil, error)
}
else if let jsonArray = response.value as? Data{
completion(jsonArray, nil)
}
}
}
Which is being called as follows:其调用如下:
executeGet() { (json, error) in
if let error = error{
print(error.localizedDescription)
}
else if let json = json {
print(type(of:json))
print(json)
let welcome = try? JSONDecoder().decode(Welcome.self, from: json)
print(welcome)
}
}
But for some reason, my 'welcome' value always returns nil.但出于某种原因,我的“欢迎”值总是返回零。 Can anyone suggest what could've gone wrong?
任何人都可以建议可能出了什么问题吗? When I
print(json)
I'm getting '294 Bytes' for some reason so clearly something went wrong before decoding, right?当我
print(json)
时,出于某种原因我得到“294 字节”,很明显在解码之前出了点问题,对吧?
EDIT: Upon Udi's request here's the Welcome
struct编辑:根据 Udi 的要求,这里是
Welcome
结构
// MARK: - Welcome
struct Welcome: Codable {
let statusCode: Int
let messageCode: String
let result: Result
}
// MARK: - Result
struct Result: Codable {
let id: String
let inputParameters: InputParameters
let robotID: String
let runByUserID, runByTaskMonitorID: JSONNull?
let runByAPI: Bool
let createdAt, startedAt, finishedAt: Int
let userFriendlyError: JSONNull?
let triedRecordingVideo: Bool
let videoURL: String
let videoRemovedAt: Int
let retriedOriginalTaskID: String
let retriedByTaskID: JSONNull?
let capturedDataTemporaryURL: String
let capturedTexts: CapturedTexts
let capturedScreenshots: CapturedScreenshots
let capturedLists: CapturedLists
enum CodingKeys: String, CodingKey {
case id, inputParameters
case robotID = "robotId"
case runByUserID = "runByUserId"
case runByTaskMonitorID = "runByTaskMonitorId"
case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
case videoURL = "videoUrl"
case videoRemovedAt
case retriedOriginalTaskID = "retriedOriginalTaskId"
case retriedByTaskID = "retriedByTaskId"
case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
case capturedTexts, capturedScreenshots, capturedLists
}
}
// MARK: - CapturedLists
struct CapturedLists: Codable {
let companies: [Company]
}
// MARK: - Company
struct Company: Codable {
let position, name, location, description: String
enum CodingKeys: String, CodingKey {
case position = "Position"
case name, location, description
}
}
// MARK: - CapturedScreenshots
struct CapturedScreenshots: Codable {
}
// MARK: - CapturedTexts
struct CapturedTexts: Codable {
let productName, width, patternRepeat, construction: String
let fiber: String
let color: JSONNull?
let mainImage: String
enum CodingKeys: String, CodingKey {
case productName = "Product Name"
case width = "Width"
case patternRepeat = "Pattern Repeat"
case construction = "Construction"
case fiber = "Fiber"
case color = "Color"
case mainImage = "Main Image"
}
}
// MARK: - InputParameters
struct InputParameters: Codable {
let originURL: String
let companiesSkip, companiesLimit: Int
enum CodingKeys: String, CodingKey {
case originURL = "originUrl"
case companiesSkip = "companies_skip"
case companiesLimit = "companies_limit"
}
}
// MARK: - Encode/decode helpers
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool
{
return true
}
public var hashValue: Int {
return 0
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
and here's a sample of JSON response这是 JSON 响应的示例
{
"statusCode": 200,
"messageCode": "success",
"result": {
"id": "f6fb62b6-f06a-4bf7-a623-c6a35c2e70b0",
"inputParameters": {
"originUrl": "https://www.ycombinator.com/companies/airbnb",
"companies_skip": 0,
"companies_limit": 10
},
"robotId": "4f5cd7ff-6c98-4cac-8cf0-d7d0cb050b06",
"runByUserId": null,
"runByTaskMonitorId": null,
"runByAPI": true,
"createdAt": 1620739118,
"startedAt": 1620739118,
"finishedAt": 1620739118,
"userFriendlyError": null,
"triedRecordingVideo": true,
"videoUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.mp4",
"videoRemovedAt": 1620739118,
"retriedOriginalTaskId": "673da019-bf0c-476e-9c4f-d35252a151dc",
"retriedByTaskId": null,
"capturedDataTemporaryUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQVG3TPBVXHSCAX63%2F20221031%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221031T185642Z&X-Amz-Expires=1800&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDfX8VNAl5kBgttrCU85U5wc1ZtSOmshO6%2FPilXOv8nvgIhAIveFfsk%2B2CnEkrMZWriodEPsj0osO5a5zV6eVu%2FXfuZKp8DCHwQAhoMMDQ1NTU3NzA4OTA3IgyrbhVK0MP1WMFBXh0q%2FAJulP5qfaV5mn3NRbINqZN4hy4Dg3IujNrZjw8ef32sWE1Gj2D%2Fc0YTJUzvx%2Fnm7LxyNO6AR35mrVy%2FBm9Q80UIspkcLMl45EK%2FoUDO0fAvoUF8g6iZ905qS3MvnOTxXkObhM1PVmpFeJFMw3jksnOPfKE4X7Ut%2FJXNwD%2F5QzdkQCXkGem%2BlrYSSSf8jB8lihTAjT%2FNXmOKMv3jktmZ13T8J1R8F8zeuLPMQf7QphUzlKn5joPb28cConluQC97y%2BjwxqIYjvIFKXY9cZEoaHGh4c6FbXsia714zG3CQp8NSGLbqCCu93oJI1Z61E%2BZ6PhB3vZGdBvXi61AlJcxZ7sti6i0h4VAbWspiJIgWwoZzrsTtneBNNpUW9tvtacGgEZIwAKV%2F3AhVEZu3WC1eQ9HtfjT9%2FjW99SEB8VVGXwkM%2FA9mtT%2FuiL0cAfQZRMhtbQJXXDRdkYEw%2FWuhjJ3zxEtEB2m3uH%2B%2BUEzOzGTd5Knm%2Bero%2BhMfN8X%2Botm3DDbtICbBjqcAf5Riii0XE1w2TZvpm%2FPNHTchCu7FnNz5hfvflv8scpgO5M4bGpy%2FadI4%2F7AUQqCQXFw4scF0FCCdb8AKJZsFGG18W1jjDHyR0YuxZFQ%2FJQRt0JP3yr%2BkVxjAH7qTtc0AzF%2FnGTgy3MOF%2Bm6Y7EkyCWyV2r6o1JTBQMftlf7MI8Uvw4cSZE6JoZviaFtmKVLGGgR4F3cDiyU56augA%3D%3D&X-Amz-Signature=a7bb4d7597ad37cdf1f260890c3c474f7f49334db58c9650d75302a34126f7bc&X-Amz-SignedHeaders=host",
"capturedTexts": {
"Product Name": "Alexis",
"Width": "15",
"Pattern Repeat": "PATTERN REPEAT",
"Construction": "Hand woven",
"Fiber": "100% Wool",
"Color": null,
"Main Image": "https://isteam.wsimg.com/ip/e31f7bba-252b-4669-9209-639d1c00765d/ols/258_original"
},
"capturedScreenshots": {
"top-ads": {
"id": "b4d132f3-12d9-4770-ac7d-88e481fc5b47",
"name": "Top ads",
"src": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"width": 600,
"height": 120,
"x": 201,
"y": 142,
"deviceScaleFactor": 1.2,
"full": "page",
"comparedToScreenshotId": "29d742c2-6f45-4f29-9d48-ba6fe66e6e3d",
"diffImageSrc": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"changePercentage": 20,
"diffThreshold": 5,
"fileRemovedAt": 1620739118
}
},
"capturedLists": {
"companies": [
{
"Position": "1",
"name": "Airbnb",
"location": "San Francisco, CA, USA",
"description": "Book accommodations around the world."
},
{
"Position": "2",
"name": "Coin base",
"location": "San Francisco, CA, USA",
"description": "Buy, sell, and manage crypto currencies."
},
{
"Position": "3",
"name": "DoorDash",
"location": "San Francisco, CA, USA",
"description": "Restaurant delivery."
}
]
}
}
}
EDIT2: Upon Rob's suggestion, I tried do
- try
- catch
, as follows: EDIT2:根据 Rob 的建议,我尝试了
do
- try
- catch
,如下所示:
executeGet() { (json, error) in
if let error = error{
print(error.localizedDescription)
}
else if let json = json {
print(type(of:json)) // Data
print(json) // 2479 Bytes
do{
var welcome = try JSONDecoder().decode(Welcome.self, from: json)
print(welcome)
}
catch {
print(error)
}
}
}
Which reports the error:其中报告错误:
keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))
keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)] , debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))
Your error was reportedly:据报道你的错误是:
keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))
keyNotFound(CodingKeys(stringValue: "companies_skip", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil), CodingKeys(stringValue: "inputParameters", intValue: nil)] , debugDescription: "No value associated with key CodingKeys(stringValue: "companies_skip", intValue: nil) ("companies_skip").", underlyingError: nil))
That points you precisely to where the decoding failed.这准确地指出了解码失败的地方。 There is apparently no key called
companies_skip
in result
» inputParameters
. result
» inputParameters
中显然没有名为companies_skip
的键。 Now, you don't show us the full response you actually received, so it is hard to be precise.现在,你没有向我们展示你实际收到的完整回复,所以很难准确。 But we can infer from this error that the response does not precisely match your sample JSON, but rather, the
companies_skip
key is not present.但是我们可以从这个错误中推断出响应与您的样本 JSON 不完全匹配,而是
companies_skip
键不存在。
We might infer from the name, inputParameters
, that your request URL (which, again, you have not shared with us) may possibly need to supply that parameter.我们可能会从名称
inputParameters
推断出您的请求 URL(同样,您没有与我们共享)可能需要提供该参数。 Or, alternatively, perhaps that parameter name shouldn't be marked as a required sub key of the inputParameters
structure (eg, you might want to make it an optional).或者,也许该参数名称不应标记为
inputParameters
结构的必需子键(例如,您可能希望将其设为可选)。
Regardless of the particulars, this is the process.不管细节如何,这就是过程。 If decoding fails, look at the complete error object, and it will tell you where it had problems.
如果解码失败,看完整的错误object,它会告诉你哪里出了问题。 Note, if there are multiple decoding problems, the error will only report the first one, so do not be surprised if it takes a few times and different queries to resolve all of the issues.
注意,如果有多个解码问题,错误只会报告第一个,所以如果需要几次不同的查询来解决所有问题,请不要感到惊讶。 The first time you start decoding a particular request, resolving all of the potential discrepancies may be an iterative process.
第一次开始解码特定请求时,解决所有潜在差异可能是一个迭代过程。
The string response you show in your comment, means you get a valid response from the server, and so you should be able to decode it with the following models.您在评论中显示的字符串响应意味着您从服务器获得了有效响应,因此您应该能够使用以下模型对其进行解码。
Use @vadian answer to your previous question: Unable to parse JSON data properly from Alomafire使用@vadian 回答你之前的问题: Unable to parse JSON data properly from Alomafire
Here are the test code and models to decode the response into a set of structs.以下是将响应解码为一组结构的测试代码和模型。
Note you will have to consult the server doc to determine which properties are Optional
and adjust the code (i,e put ?
) where nessesary.请注意,您必须查阅服务器文档以确定哪些属性是
Optional
的,并在必要时调整代码(即放置?
)。
struct ContentView: View {
@State var welcome: WelcomeResponse?
var body: some View {
VStack {
if let response = welcome {
Text(response.messageCode)
Text("\(response.statusCode)")
ForEach(response.result.capturedLists.companies) { item in
Text(item.description)
}
}
}
.onAppear {
let json = """
{
"statusCode": 200,
"messageCode": "success",
"result": {
"id": "f6fb62b6-f06a-4bf7-a623-c6a35c2e70b0",
"inputParameters": {
"originUrl": "https://www.ycombinator.com/companies/airbnb",
"companies_skip": 0,
"companies_limit": 10
},
"robotId": "4f5cd7ff-6c98-4cac-8cf0-d7d0cb050b06",
"runByUserId": null,
"runByTaskMonitorId": null,
"runByAPI": true,
"createdAt": 1620739118,
"startedAt": 1620739118,
"finishedAt": 1620739118,
"userFriendlyError": null,
"triedRecordingVideo": true,
"videoUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.mp4",
"videoRemovedAt": 1620739118,
"retriedOriginalTaskId": "673da019-bf0c-476e-9c4f-d35252a151dc",
"retriedByTaskId": null,
"capturedDataTemporaryUrl": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/system-1620230966-b1a9688b-05d3-4682-beeb-9ce035e482b1.json?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQVG3TPBVXHSCAX63%2F20221031%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221031T185642Z&X-Amz-Expires=1800&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEJP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQDfX8VNAl5kBgttrCU85U5wc1ZtSOmshO6%2FPilXOv8nvgIhAIveFfsk%2B2CnEkrMZWriodEPsj0osO5a5zV6eVu%2FXfuZKp8DCHwQAhoMMDQ1NTU3NzA4OTA3IgyrbhVK0MP1WMFBXh0q%2FAJulP5qfaV5mn3NRbINqZN4hy4Dg3IujNrZjw8ef32sWE1Gj2D%2Fc0YTJUzvx%2Fnm7LxyNO6AR35mrVy%2FBm9Q80UIspkcLMl45EK%2FoUDO0fAvoUF8g6iZ905qS3MvnOTxXkObhM1PVmpFeJFMw3jksnOPfKE4X7Ut%2FJXNwD%2F5QzdkQCXkGem%2BlrYSSSf8jB8lihTAjT%2FNXmOKMv3jktmZ13T8J1R8F8zeuLPMQf7QphUzlKn5joPb28cConluQC97y%2BjwxqIYjvIFKXY9cZEoaHGh4c6FbXsia714zG3CQp8NSGLbqCCu93oJI1Z61E%2BZ6PhB3vZGdBvXi61AlJcxZ7sti6i0h4VAbWspiJIgWwoZzrsTtneBNNpUW9tvtacGgEZIwAKV%2F3AhVEZu3WC1eQ9HtfjT9%2FjW99SEB8VVGXwkM%2FA9mtT%2FuiL0cAfQZRMhtbQJXXDRdkYEw%2FWuhjJ3zxEtEB2m3uH%2B%2BUEzOzGTd5Knm%2Bero%2BhMfN8X%2Botm3DDbtICbBjqcAf5Riii0XE1w2TZvpm%2FPNHTchCu7FnNz5hfvflv8scpgO5M4bGpy%2FadI4%2F7AUQqCQXFw4scF0FCCdb8AKJZsFGG18W1jjDHyR0YuxZFQ%2FJQRt0JP3yr%2BkVxjAH7qTtc0AzF%2FnGTgy3MOF%2Bm6Y7EkyCWyV2r6o1JTBQMftlf7MI8Uvw4cSZE6JoZviaFtmKVLGGgR4F3cDiyU56augA%3D%3D&X-Amz-Signature=a7bb4d7597ad37cdf1f260890c3c474f7f49334db58c9650d75302a34126f7bc&X-Amz-SignedHeaders=host",
"capturedTexts": {
"Product Name": "Alexis",
"Width": "15",
"Pattern Repeat": "PATTERN REPEAT",
"Construction": "Hand woven",
"Fiber": "100% Wool",
"Color": null,
"Main Image": "https://isteam.wsimg.com/ip/e31f7bba-252b-4669-9209-639d1c00765d/ols/258_original"
},
"capturedScreenshots": {
"top-ads": {
"id": "b4d132f3-12d9-4770-ac7d-88e481fc5b47",
"name": "Top ads",
"src": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"width": 600,
"height": 120,
"x": 201,
"y": 142,
"deviceScaleFactor": 1.2,
"full": "page",
"comparedToScreenshotId": "29d742c2-6f45-4f29-9d48-ba6fe66e6e3d",
"diffImageSrc": "https://prod-browseai-captured-data.s3.amazonaws.com/1fae674a-2788-46a8-83c8-95c4664c6d25/6326b3c1-7b16-4256-a323-7d8d8954bd4e/1061671f-7f71-42ac-bb9a-207d126d1f3a/00001-user-1620230947-6f113cf2-90ef-4c66-a448-9d5c6bd64873.png",
"changePercentage": 20,
"diffThreshold": 5,
"fileRemovedAt": 1620739118
}
},
"capturedLists": {
"companies": [
{
"Position": "1",
"name": "Airbnb",
"location": "San Francisco, CA, USA",
"description": "Book accommodations around the world."
},
{
"Position": "2",
"name": "Coin base",
"location": "San Francisco, CA, USA",
"description": "Buy, sell, and manage crypto currencies."
},
{
"Position": "3",
"name": "DoorDash",
"location": "San Francisco, CA, USA",
"description": "Restaurant delivery."
}
]
}
}
}
"""
// simulated API data from the server
let data = json.data(using: .utf8)!
do {
let results = try JSONDecoder().decode(WelcomeResponse.self, from: data)
welcome = results
print("\n---> results: \(results) \n")
} catch {
print("\n---> decoding error: \n \(error)\n")
}
}
}
}
// MARK: - WelcomeResponse
struct WelcomeResponse: Codable {
let statusCode: Int
let messageCode: String
let result: Result
}
// MARK: - Result
struct Result: Codable {
let id: String
let inputParameters: InputParameters
let robotID: String
let runByUserID, runByTaskMonitorID: String?
let runByAPI: Bool
let createdAt, startedAt, finishedAt: Int
let userFriendlyError: String?
let triedRecordingVideo: Bool
let videoURL: String
let videoRemovedAt: Int
let retriedOriginalTaskID: String
let retriedByTaskID: String?
let capturedDataTemporaryURL: String
let capturedTexts: CapturedTexts
let capturedScreenshots: CapturedScreenshots
let capturedLists: CapturedLists
enum CodingKeys: String, CodingKey {
case id, inputParameters
case robotID = "robotId"
case runByUserID = "runByUserId"
case runByTaskMonitorID = "runByTaskMonitorId"
case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
case videoURL = "videoUrl"
case videoRemovedAt
case retriedOriginalTaskID = "retriedOriginalTaskId"
case retriedByTaskID = "retriedByTaskId"
case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
case capturedTexts, capturedScreenshots, capturedLists
}
}
// MARK: - CapturedLists
struct CapturedLists: Codable {
let companies: [Company]
}
// MARK: - Company
struct Company: Identifiable, Codable {
let id = UUID()
let position, name, location, description: String
enum CodingKeys: String, CodingKey {
case position = "Position"
case name, location, description
}
}
// MARK: - CapturedScreenshots
struct CapturedScreenshots: Codable {
let topAds: TopAds
enum CodingKeys: String, CodingKey {
case topAds = "top-ads"
}
}
// MARK: - TopAds
struct TopAds: Codable {
let id, name: String
let src: String
let width, height, x, y: Int
let deviceScaleFactor: Double
let full, comparedToScreenshotId: String
let diffImageSrc: String
let changePercentage, diffThreshold, fileRemovedAt: Int
}
// MARK: - CapturedTexts
struct CapturedTexts: Codable {
let productName, width, patternRepeat, construction: String
let fiber: String
let color: String?
let mainImage: String
enum CodingKeys: String, CodingKey {
case productName = "Product Name"
case width = "Width"
case patternRepeat = "Pattern Repeat"
case construction = "Construction"
case fiber = "Fiber"
case color = "Color"
case mainImage = "Main Image"
}
}
// MARK: - InputParameters
struct InputParameters: Codable {
let originUrl: String
let companiesSkip: Int?
let companiesLimit: Int?
enum CodingKeys: String, CodingKey {
case originUrl
case companiesSkip = "companies_skip"
case companiesLimit = "companies_limit"
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.