I have one API where based on parameters data is changing.
Response 1
{
"success": true,
"statusCode": 200,
"errorLst": [],
"succcessMessage": null,
"resultData": [
{
"plotSize": 1,
"equationValue": null
},
{
"plotSize": 2,
"equationValue": "*1.5"
},
{
"plotSize": 3,
"equationValue": "*2.5"
}
]
}
Response 2
{
"success": true,
"statusCode": 200,
"errorLst": [],
"succcessMessage": null,
"resultData": [
{
"plotSize": 4,
"equationValue": "*4"
},
{
"plotSize": 5,
"equationValue": "*5.5"
},
{
"plotSize": 6,
"equationValue": "*6.5"
}
]
}
I parse the API data and try to convert the base model data to my actual mode data:
let myEndPoint = EndPoint(modelType: PlotModel.self)
self.plotModelModel = convertToModel(baseModelResponse: inner_apiResponse, endpoint: myEndPoint) as? PlotModel ?? PlotModel()
inner_apiResponse
is nothing but the data I get from API parsed as BaseModel
Model I have is as below:
struct PlotModel : Codable {
var success : Bool?
var statusCode : Int?
var succcessMessage : String?
var resultData : [InnerPlotData]?
}
struct InnerPlotData : Codable {
var plotSize : Int?
var equationValue : String?
}
I am using below function to convert the base model to my model:
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()
}
}
Model I have is as below:
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)
}
}
}
Response 2 is working fine, however Response 1 is giving error as below:
error.debugDescription==The data couldn’t be written because it isn’t in the correct format.
Exception is thrown at let jsonData = try encoder.encode(baseModelResponse)
line.
Why is this happening and how can it be fixed?
https://wetransfer.com/downloads/08ebea0e519f58023444c36727e4afdc20240820150159/adf188
After further check as per vadian, I found that in func encode
, I get exception as below.
invalidValue("InvalidValue", Swift.EncodingError.Context(codingPath: [CodingKeys(stringValue: "resultData", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), _JSONKey(stringValue: "equationValue", intValue: nil)], debugDescription: "AnyEncodable value cannot be encoded", underlyingError: nil))
However the equationValue
is of type String as mentioned in the model
The value that's failing is:
"equationValue": null
You forgot to include a case for that:
case nil:
try container.encodeNil()
A better approach for you may be a JSON type that can hold "something that can be expressed in JSON" without resorting to Any
.