Reputation: 125
I have some REST API
, which returns JSON
. This API is used by more than one service (mobile, web,...) and it returns JSON
that has more fields inside than I need and some of those additional fields can be changed on weekly basis.
For example I have JSON like that:
{ "name": "Toyota Prius", "horsepower": 134, "mileage": 123241, "manufactured": 2017, "location": "One city", "origin": "Japan",
"convert": true //more properties follows... }
In mobile app I would only need to decode fields: name, horsepower and manufactured date, the rest can be ignored. Also some fields like origin haven't existed before but was added few days ago.
Question is how to write bulletproof decoder that would not break if there are some changes in JSON
that do not affect my model in app? Also I have no word in output JSON
from server and I cannot affect it. I just get data and I have to work with what I get.
I have wrote some experimental code for playground (car isn't printed out):
import UIKit
struct Car: Codable
{
let name: String
let horsePower: Int
let selected: Bool //this is used as additional property inside my app to mark selected cars
let index: Int //this is used as additional property inside my app for some indexing purposes
enum CodingKeys: String, CodingKey
{
case name
case horsePower = "horsepower"
case selected
case index
}
init(from decoder: Decoder) throws
{
let values = try decoder.container(keyedBy: CodingKeys.self)
selected = false
index = 0
name = try values.decode(String.self, forKey: .name)
horsePower = try values.decode(Int.self, forKey: .horsePower)
}
}
let json = "{\"name\": \"Toyota Prius\",\"horsepower\": 134, \"convert\", true}"
let data = json.data(using: .utf8)
if let car = try? JSONDecoder().decode(Car.self, from: data!)
{
//does not print... :(
print(car)
}
I would like to get car printed in my example but mostly have a working proof code that will not break if JSON
get changed.
Also is there a way to get an decoding error description somehow?
I know there are a lot of things in apple documentation, but mostly it is just too confusing to me and I couldn't find any useful examples for my problem.
Upvotes: 8
Views: 5155
Reputation: 285069
First of all never try? JSONDecoder...
, catch
always the error and print it. DecodingErrors are extremely descriptive. They tell you exactly what is wrong and even where.
In your example you will get
"The given data was not valid JSON. ... No value for key in object around character 52."
which is the wrong comma (instead of a colon) after convert\"
To decode only specific keys declare the CodingKeys
accordingly and delete the init
method. selected
and index
are most likely supposed to be mutable so declare them as variable with a default value.
If the backend changes the JSON structure you'll get an error. The decoding process will break anyway regardless of the parsing API.
struct Car: Codable
{
let name: String
let horsePower: Int
let convert : Bool
var selected = false
var index = 0
enum CodingKeys: String, CodingKey {
case name, horsePower = "horsepower", convert
}
}
let json = """
{"name":"Toyota Prius","horsepower":134,"convert":true}
"""
let data = Data(json.utf8)
do {
let car = try JSONDecoder().decode(Car.self, from: data)
print(car)
} catch { print(error) }
Upvotes: 9