Encoding and decoding JSON in Swift
JSON data
The JSON format (JavaScript Object Notation) is very well suited for the exchange of structured data due to its easily readable text form. Data is represented via associative arrays {...} and lists [...]:
[
{
"name": "Bob",
"age": 32,
"phone": [
{ "type": "mobile", "number": "0123-456789" },
{ "type": "work", "number": "040-456789" }
]
},
{
"name": "Alice",
"age": 56,
"address": {
"street": "Musterstrasse 12",
"zip": "20095",
"city": "Hamburg"
}
}
]
Handling JSON data in Swift
With the Foundation classes JSONDecoder and JSONEncoder, JSON data can be parsed in Swift and Swift values can be converted to JSON. It is recommended to define suitable data types as struct, which correspond 1:1 to the structure from the JSON. These must be declared conforming to the Codable protocol:
import Foundation
struct Person: Codable {
let name: String
let age: Int
let phone: [Phone]?
let address: Address?
}
struct Address: Codable {
let street, zip, city: String
}
struct Phone: Codable {
let type, number: String
}
A JSON file included in the Xcode project and shipped with the app could be parsed as follows:
if let jsonURL = Bundle.main.url(forResource: "persons", withExtension: "json") {
let jsonData = try Data(contentsOf: jsonURL)
let jsonDecoder = JSONDecoder()
let persons = try jsonDecoder.decode([Person].self, from: jsonData)
}
A Codable object can be encoded as JSON as follows:
let jsonEncoder = JSONEncoder()
let jsonResultData = try jsonEncoder.encode(persons)
Customize names of properties, additional properties
A CodingKeys enum can be used to explicitly set the names of the properties. For this, it is necessary to include all properties that should be included in the JSON output. Properties that are not contained in CodingKeys will be ignored. This allows properties to be added to a type that should not be included in the JSON output:
struct Person: Codable {
let name: String
let age: Int
var someOtherAttribute: String?
enum CodingKeys: String, CodingKey {
case name = "name"
case age = "ageYears"
}
}
Naming of properties: Snake-case vs. Camel-case
Properties in JSON data often follow the Snake-case naming convention (e.g. phone_number). Following the Swift naming conventions, a corresponding property would be named according to the Camel-case naming convention (e.g. phoneNumber). Conveniently, a corresponding conversion of the naming conventions can be configured:
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
jsonEncoder.keyEncodingStrategy = .convertToSnakeCase
Formatted output
With outputFormatting the output of the JSON serialization can be configured:
jsonEncoder.outputFormatting = .prettyPrinted
Date values
For the non-standard encoding of date values in JSON, a corresponding option can be set:
jsonEncoder.dateEncodingStrategy = .iso8601
jsonDecoder.dateDecodingStrategy = .iso8601
Customize the encoding
If the structure of the Swift type differs from its encoded form, it is possible to define your own encoding and decoding logic:
Adjusting the encoding logic is only useful up to a point. For very irregularly structured JSON data formats, the JSONSerialization class might be an alternative. This doesn't map to Swift types but returns values as dictionaries. Another way to deal with such data is to use the open-source framework SwiftyJSON, which provides a convenient API for dynamic access to JSON data.
Tools
Generate Codable types
With quicktype Swift code for types can be generated using sample JSON data:
This can also be done using a command line tool, for example:
brew install quicktype quicktype https://www.ralfebert.de/examples/v3/countries.json --lang swift --no-initializers --no-coding-keys --density normal --acronym-style camel
Display JSON data in a structured way
The plug-ins JSONView for Chrome/Firefox or SimplyJSON for Safari can be used to display JSON data formatted in the browser: