Trying to write a post in English. 👻
Now Apple support parsing JSON directly in Swift. I write an example to show how to do.Download Demo
Before Swift 4 1
2
3
4
5
6
7
let aBand: [String : Any ] = [
"id" : 23 ,
"name" : "Nirvana"
]
let data = try JSONSerialization .data(withJSONObject: aBand, options: .prettyPrinted)
Object Code:
1
2
3
4
5
6
7
8
9
10
struct Band {
let id: Int
let name: String ?
init (json: [String : Any ]) {
id = json["id" ] as ? Int ?? -1
name = json["name" ] as ? String ?? ""
}
}
Here is what we do in Swift 2/3 or ObjC:
1
2
3
4
5
6
7
8
9
do {
guard let dict = try JSONSerialization .jsonObject(with: data, options: .mutableContainers) as ? [String : Any ] else {
throw NSError ()
}
let band = Band (json: dict)
}
catch let error {
print (error)
}
Swift 4 Dictionary structure 1
2
3
4
let aBand: [String : Any ] = [
"id" : 23 ,
"name" : "Nirvana"
]
1
2
3
4
5
6
7
8
9
10
11
12
struct Band : Decodable {
let id: Int
let name: String
}
do {
let band = try JSONDecoder ().decode(Band .self , from: data)
print (band)
}
catch let error {
print (error)
}
Result: Band(id: 23, name: "Nirvana")
Array structure 1
2
3
4
5
6
7
8
9
10
let bands: [[String : Any ]] = [
[
"id" : 23 ,
"name" : "Nirvana"
],
[
"id" : 22 ,
"name" : "The Beatles"
]
]
Just change Band.self
to [Band].self
1
let band = try JSONDecoder ().decode([Band ].self , from: data)
Result: [JsonDecode.Band(id: 23, name: "Nirvana"), JsonDecode.Band(id: 22, name: "The Beatles")]
Nested structure 1
2
3
4
5
6
7
8
9
10
11
12
13
let type: [String : Any ] = [
"name" : "Rock" ,
"examples" : [
[
"id" : 23 ,
"name" : "Nirvana"
],
[
"id" : 22 ,
"name" : "The Beatles"
]
]
]
Create a new type:
1
2
3
4
struct MusicType : Decodable {
let name: String
let examples: [Band ]
}
1
2
3
4
5
6
7
8
9
10
11
12
struct Band : Decodable {
let id: Int
let name: String
}
do {
let band = try JSONDecoder ().decode(MusicType .self , from: data)
print (band)
}
catch let error {
print (error)
}
Result: MusicType(name: "Rock", examples: [JsonDecode.Band(id: 23, name: "Nirvana"), JsonDecode.Band(id: 22, name: "The Beatles")])
Optional field What if we got a JSON like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let type: [String : Any ] = [
"name" : "Rock" ,
"examples" : [
[
"id" : 23 ,
],
[
"id" : 22 ,
"name" : "The Beatles" ,
"country" : "UK"
]
]
]
OK, obviously the decoder will ignore the “country” key, but can not find the key “name”, we will get a error:
1
2
3
4
5
keyNotFound(CodingKeys(stringValue: "name", intValue: nil),
Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "examples", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)],
debugDescription: "No value associated with key CodingKeys(stringValue: \"name\", intValue: nil) (\"name\").",
underlyingError: nil))
All we need to do is to make the name
property to Optional:
1
2
3
4
struct Band : Decodable {
let id: Int
let name: String ?
}
Result: MusicType(name: "Rock", examples: [JsonDecode.Band(id: 23, name: nil), JsonDecode.Band(id: 22, name: Optional("The Beatles"))])
Custom keys If the field in JSON is not what we want:
1
2
3
4
let aBand: [String : Any ] = [
"id" : 23 ,
"band_name" : "Nirvana"
]
We need to define a nested enum in our type called “CodingKeys” (or use a typealias with this name) that conforms to the CodingKey protocol – Swift will automatically use this as the key type. This therefore allows you to easily customise the keys that your properties are decoded with.
1
2
3
4
5
6
7
8
9
struct Band : Decodable {
let id: Int
let name: String
private enum CodingKeys : String , CodingKey {
case id
case name = "band_name"
}
}
Results: Band(id: 23, name: "Nirvana")
But we need to write all the other properties in the enum, what we expected is adding others automatically in compiling time. And also we expect a many-to-one ability, like "band_name" | "bandName" => name
. Hope Apple to update.
Update on 30th March, 2018 for Swift 4.1
1
2
3
4
5
6
7
8
struct Band : Decodable {
let id: Int
let bandName: String
}
let decoder = JSONDecoder ()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let band = try ! decoder.decode(Band .self , from: data)
Results: Band(id: 23, name: "Nirvana")
Encode Same like Decodable
, We can use Encodable
, or just use Codable
1
public typealias Codable = Decodable & Encodable
__Original articles, please indicate the source if you reproduced it.