У меня есть один API, где данные параметров меняются в зависимости от параметров.
Ответ 1
{
"success": true,
"statusCode": 200,
"errorLst": [],
"succcessMessage": null,
"resultData": [
{
"plotSize": 1,
"equationValue": null
},
{
"plotSize": 2,
"equationValue": "*1.5"
},
{
"plotSize": 3,
"equationValue": "*2.5"
}
]
}
Ответ 2
{
"success": true,
"statusCode": 200,
"errorLst": [],
"succcessMessage": null,
"resultData": [
{
"plotSize": 4,
"equationValue": "*4"
},
{
"plotSize": 5,
"equationValue": "*5.5"
},
{
"plotSize": 6,
"equationValue": "*6.5"
}
]
}
Я анализирую данные API и пытаюсь преобразовать данные базовой модели в данные моего фактического режима:
let myEndPoint = EndPoint(modelType: PlotModel.self)
self.plotModelModel = convertToModel(baseModelResponse: inner_apiResponse, endpoint: myEndPoint) as? PlotModel ?? PlotModel()
inner_apiResponse
— это не что иное, как данные, которые я получаю от API, анализируемые как BaseModel
У меня есть модель, как показано ниже:
struct PlotModel : Codable {
var success : Bool?
var statusCode : Int?
var succcessMessage : String?
var resultData : [InnerPlotData]?
}
struct InnerPlotData : Codable {
var plotSize : Int?
var equationValue : String?
}
Я использую функцию ниже для преобразования базовой модели в мою модель:
func convertToModel<T : Codable>(baseModelResponse : BaseModel, endpoint : EndPoint<T>) -> Any {
do {
let encoder = JSONEncoder()
do {
let jsonData = try encoder.encode(baseModelResponse)
if let jsonString = String(data: jsonData, encoding: .utf8) {
if let data = jsonString.data(using: .utf8) {
do {
let localModel = try JSONDecoder().decode(endpoint.modelType.self, from: data)
return localModel
} catch {
print("Error parsing JSON:", error)
return makeErrorBaseModel()
}
} else {
print("Invalid JSON string")
return makeErrorBaseModel()
}
} else {
return makeErrorBaseModel()
}
} catch let decodingError as DecodingError {
switch decodingError {
case .typeMismatch(_, let c), .valueNotFound(_, let c), .keyNotFound(_, let c), .dataCorrupted(let c):
"error.debugDescription=c===\(c.debugDescription)".printLog()
return makeErrorBaseModel()
}
} catch {
"error.debugDescription==\(error.localizedDescription)".printLog()
return makeErrorBaseModel()
}
} catch {
return makeErrorBaseModel()
}
}
У меня есть модель, как показано ниже:
struct BaseModel : Codable {
var success : Bool?
var statusCode : Int?
var succcessMessage : String?
var resultData : AnyCodable?
}
@objcMembers final class AnyCodable: NSObject, Codable {
let value: Any?
init(_ value: Any?) {
self.value = value
}
required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let value = try? container.decode(String.self) {
self.value = value
} else if let value = try? container.decode(Bool.self) {
self.value = value
} else if let value = try? container.decode(Int.self) {
self.value = value
} else if let value = try? container.decode(Double.self) {
self.value = value
} else if let value = try? container.decode(Float.self) {
self.value = value
} else if container.decodeNil() {
self.value = nil
} else if let value = try? container.decode([String: AnyCodable].self) {
self.value = value.mapValues { $0.value }
} else if let value = try? container.decode([AnyCodable].self) {
self.value = value.map { $0.value }
} else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Invalid value cannot be decoded"
)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch value {
case let bool as Bool:
try container.encode(bool)
case let int as Int:
try container.encode(int)
case let float as Float:
try container.encode(float)
case let double as Double:
try container.encode(double)
case let string as String:
try container.encode(string)
case let array as [Any?]:
try container.encode(array.map { AnyCodable($0) })
case let dictionary as [String: Any?]:
try container.encode(dictionary.mapValues { AnyCodable($0) })
case let encodable as Encodable:
try encodable.encode(to: encoder)
default:
let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded")
throw EncodingError.invalidValue(value ?? "InvalidValue", context)
}
}
}
Ответ 2 работает нормально, однако ответ 1 выдает ошибку, как показано ниже:
error.debugDescription==The data couldn’t be written because it isn’t in the correct format.
Исключение выдается в строке let jsonData = try encoder.encode(baseModelResponse)
.
Почему это происходит и как это можно исправить?
https://wetransfer.com/downloads/08ebea0e519f58023444c36727e4afdc20240820150159/adf188
После дальнейшей проверки согласно вадиану я обнаружил, что в func encode
я получаю исключение, как показано ниже.
validValue("InvalidValue", Swift.EncodingError.Context(codingPath: [CodingKeys(stringValue: "resultData", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), _JSONKey(stringValue: "equationValue", intValue: nil)], debugDescription: «Значение AnyEncodable не может быть закодировано», базовая ошибка: nil))
Однако equationValue
имеет тип String, как указано в модели.
Не могли бы вы предоставить MRE ?
@JoakimDanielson: Хорошо, я сделаю это сегодня, когда освобожусь...
Возможно, я что-то упускаю, но почему бы вам просто не декодировать тело ответа напрямую, пытаясь получить PlotModel
? Или, может быть, используйте общий Response<T>
, соответствующий Decodeable, где T
— декодируемый тип того, что есть resultData
. Затем вы можете «развернуть» фактические данные типа T
со свойством resultData
и положить этому конец ;) Значит, вам больше ничего не нужно — только это Result<T>
. Просто убедитесь, что T
также соответствует декодируемому.
В среде Codable
всегда печатайте error
, а не его localizedDescription
. Вы отбрасываете настоящую описательную ошибку.
@JoakimDanielson : wetransfer.com/downloads/… Пожалуйста, проверьте apiURLString и журналы на наличие ошибок...
@vadian: Я обновил вопрос со ссылкой на код... Можете ли вы проверить, если возможно...
@vadian: спасибо за сохранение... Я получаю ошибку "invalidValue("InvalidValue", Swift.EncodingError.Context(codingPath: [CodingKeys(stringValue: "resultData", intValue: nil), _JSONKey(stringValue: "Index 0) ", intValue: 0), _JSONKey(stringValue: "equationValue", intValue: nil)], debugDescription: "Любое кодируемое значение не может быть закодировано", базовая ошибка: ноль))"
@JoakimDanielson: В своем коде я не добавил var equationValue : String?
внутри RentEstimatorInnerVisibleConfigPlotSizesModel
модели. Вы можете добавить, чтобы проверить дальше. Я не добавлял, так как не хочу им пользоваться, он для меня бесполезен. Также проверьте сообщение выше, чтобы проверить причину исключения...
Ошибка означает то, что написано: «Значение AnyEncodable не может быть закодировано». Вы боретесь с фреймворком. Codable
не поддерживает Any
. Традиционный JSONSerialization
не вариант?
Значение, которое терпит неудачу:
"equationValue": null
Вы забыли указать случай для этого:
case nil:
try container.encodeNil()
Лучшим подходом для вас может быть тип JSON, который может содержать «что-то, что можно выразить в JSON», не прибегая к Any
.
Спасибо за быстрый ответ. Я изучу это и отвечу вам...
Если вы заметили, уравнение ValueValue имеет нулевое значение в первой строке, и
AnyCodable
устанавливает его тип какAny
, а в следующем месте я получаю данные какString
, поэтому я получаю эту ошибку. Может быть...