The JSONSerialization class is built into Apple's Foundation framework.
The JSONObjectWithData
function takes NSData
, and returns AnyObject
. You can use as?
to convert the result to your expected type.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".dataUsingEncoding(NSUTF8StringEncoding) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joinWithSeparator(", "))")
}
} catch {
print("error reading JSON: \(error)")
}
You can pass options: .AllowFragments
instead of options: []
to allow reading JSON when the top-level object isn't an array or dictionary.
Calling dataWithJSONObject
converts a JSON-compatible object (nested arrays or dictionaries with strings, numbers, and NSNull
) to raw NSData
encoded as UTF-8.
do {
// Convert object to JSON as NSData
let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
You can pass options: .PrettyPrinted
instead of options: []
for pretty-printing.
Same behavior in Swift 3 but with a different syntax.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".data(using: String.Encoding.utf8) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joined(separator: ", "))")
}
} catch {
print("error reading JSON: \(error)")
}
do {
// Convert object to JSON as NSData
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: .utf8)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
Note: The Following is currently available only in Swift 4.0 and later.
As of Swift 4.0, the Swift standard library includes the protocols Encodable
and Decodable
to define a standardized approach to data encoding and decoding. Adopting these protocols will allow implementations of the Encoder
and Decoder
protocols take your data and encode or decode it to and from an external representation such as JSON. Conformance to the Codable
protocol combines both the Encodable
and Decodable
protocols. This is now the recommended means to handle JSON in your program.
The easiest way to make a type codable is to declare its properties as types that are already Codable
. These types include standard library types such as String
, Int
, and Double
; and Foundation types such as Date
, Data
, and URL
. If a type's properties are codable, the type itself will automatically conform to Codable
by simply declaring the conformance.
Consider the following example, in which the Book
structure conforms to Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Note that standard collections such as
Array
andDictionary
conform toCodable
if they contain codable types.
By adopting Codable
, the Book
structure can now be encoded to and decoded from JSON using the Apple Foundation classes JSONEncoder
and JSONDecoder
, even though Book
itself contains no code to specifically handle JSON. Custom encoders and decoders can be written, as well, by conforming to the Encoder
and Decoder
protocols, respectively.
// Create an instance of Book called book
let encoder = JSONEncoder()
let data = try! encoder.encode(book) // Do not use try! in production code
print(data)
Set
encoder.outputFormatting = .prettyPrinted
for easier reading. ## Decode from JSON data
// Retrieve JSON string from some source
let jsonData = jsonString.data(encoding: .utf8)!
let decoder = JSONDecoder()
let book = try! decoder.decode(Book.self, for: jsonData) // Do not use try! in production code
print(book)
In the above example,
Book.self
informs the decoder of the type to which the JSON should be decoded.
Sometimes you may not need data to be both encodable and decodable, such as when you need only read JSON data from an API, or if your program only submits JSON data to an API.
If you intend only to write JSON data, conform your type to Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
If you intend only to read JSON data, conform your type to Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
APIs frequently use naming conventions other than the Swift-standard camel case, such as snake case. This can become an issue when it comes to decoding JSON, since by default the JSON keys must align exactly with your type's property names. To handle these scenarios you can create custom keys for your type using the CodingKey
protocol.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
are generated automatically for types which adopt the Codable
protocol, but by creating our own implementation in the example above we're allow our decoder to match the local camel case publicationDate
with the snake case publication_date
as it's delivered by the API.