I have this JSON coming from server:
{
"models": [
{
"code": "RPX",
"name": "Tank",
"color": null,
"alias": [
"tank"
],
"plantCode": "FR",
"countryName": "France",
"plantAlias": null,
"plantGroup": "5",
"matrix": [
{
"size": "BIG",
"union": null
}
],
"imageURL": "https://example.com/tank.jpg"
}
]
}
from this JSON I have built this model:
import Foundation
// MARK: - Welcome
struct ModelDecode: Codable {
let models: [Model]?
}
// MARK: - Model
struct Model: Codable {
let code, name: String?
let color: JSONNull?
let alias: [String]?
let plantCode, countryName: String?
let plantAlias: JSONNull?
let plantGroup: String?
let matrix: [Matrix]?
let imageURL: String?
}
// MARK: - Matrix
struct Matrix: Codable {
let size: String?
let union: JSONNull?
}
// 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 func hash(into hasher: inout Hasher) {
// No-op
}
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()
}
}
Then I have this to decode the JSON:
final class ListOfTanks:NSObject, URLSessionDelegate {
private let session: URLSession = NetworkService.shared.getSession()
private let decoder = JSONDecoder()
private let encoder = JSONEncoder()
static let shared = ListOfTanks()
static func serverURL() -> URL? {
let getServerHost = "https://example.com/request"
let getServerPath = "/getTanks"
var components = URLComponents()
components.scheme = "https"
components.host = getServerHost
components.path = getServerPath
guard let url = components.url else { return nil }
return url
}
static func getRequest() -> URLRequest? {
guard let url = ListOfTanks.serverURL()
else { return nil }
var request: URLRequest = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("service", forHTTPHeaderField: "iPad")
request.httpMethod = "GET"
return request
}
func getStations(completion:callback<ModelDecode>?) {
guard let request = ListOfTanks.getRequest() else {
completion?(.failure("Failed to build request"))
return }
let session: URLSession = URLSession(configuration: URLSessionConfiguration.default,
delegate: self,
delegateQueue: OperationQueue())
let task = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
guard let data = data else {
completion?(.failure("Error"))
return
}
do {
let result = try self.decoder.decode(ModelDecode.self, from: data)
completion?(.success(result))
} catch (let error) {
print(error)
completion?(.failure("Error Parsing Error \(String(describing:error))"))
}
}
task.resume()
}
}
the JSON is retrieved correctly but the last try
for decoding fails with:
typeMismatch(MyApp.JSONNull, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "models", intValue: nil), _JSONKey(stringValue: "Index 1", intValue: 1), CodingKeys(stringValue: "matrix", intValue: nil), _JSONKey(stringValue: "Index 1", intValue: 1), CodingKeys(stringValue: "union", intValue: nil)], debugDescription: "Wrong type for JSONNull", underlyingError: nil))
failure("Error Parsing Error typeMismatch(MyApp.JSONNull, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: \"models\", intValue: nil), _JSONKey(stringValue: \"Index 1\", intValue: 1), CodingKeys(stringValue: \"matrix\", intValue: nil), _JSONKey(stringValue: \"Index 1\", intValue: 1), CodingKeys(stringValue: \"union\", intValue: nil)], debugDescription: \"Wrong type for JSONNull\", underlyingError: nil))")
How do I solve that?
NOTE: I have omitted some code that I think is implicit, once the JSON is being retrieved correctly. If you need some specific code, please ask.
CodePudding user response:
The first thing you should do is get rid of that JSONNull
type. It´s not helping. This type will fail to decode as soon as the JSON value is something different then null
. And a property that allways stays null
seems kind of pointless.
Next step would be to replace the JSONNull
s with their "real" type as an optional. Multiple approaches here
Get a better sample. As you seem to have created this structs by quicktype.io, paste a larger dataset into it. There´s a good chance this will give you the types you need.
Make an assumption. As all of the properties seem to be of type
String
it would be (kind of) safe to assume the missing ones are of the same type. ReplaceJSONNull
withString?
Ask/Read. Read the docu or contact the owner/developer of the API and ask what types there are and wich one could get
null
.