Hello dear colleagues and those who work with iOS. If you have some basic awareness about the Decodable protocol, you can parse a simple JSON in a minute and lots of sites have multiple examples of how to parse JSON. Certain problems begin, when you receive JSON with several levels of nesting, it’s time when you have to sweat a bit to parse it. Here I’d like to share my experience of parsing the JSON with several levels of nesting in coreData. I will not describe how to make a request in order to receive data from the server, I will just use JSON, which is stored on the disk and which we will parse and record into the CoreDate.
Experience of Parsing the JSON
So we have a small JSON, it has certain objects to be displayed under the key result. These objects in their turn have a massive of friends, which should also be displayed.
{"studios":["Marvel", "Warner Brothers", "DC"], "results":[{"name":"Daffy Duck", "id":548001360, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ7leBgakzSpfs50MeDnkd39Lu_nZpyHIE_0tm3FzovjaGhiF7K", "friends":[{"name":"Bugs Bunny", "id":825382838, "avatarPath":"https://avatarfiles.alphacoders.com/812/81220.webp"}, {"name":"Porky Pig", "id":819263082, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTEQcXKKj5YfDTIkNPcBBNyLrCv7M5V4LiwFh1n8VJy6H5RJrBKew"}]}, {"name":"Porky Pig", "id":819263082, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTEQcXKKj5YfDTIkNPcBBNyLrCv7M5V4LiwFh1n8VJy6H5RJrBKew", "friends":[{"name":"Bugs Bunny", "id":825382838, "avatarPath":"https://avatarfiles.alphacoders.com/812/81220.webp"}, {"name":"Daffy Duck", "id":548001360, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ7leBgakzSpfs50MeDnkd39Lu_nZpyHIE_0tm3FzovjaGhiF7K"}]}, {"name":"Bugs Bunny", "id":825382838, "avatarPath":"https://avatarfiles.alphacoders.com/812/81220.webp", "friends":[{"name":"Porky Pig", "id":819263082, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTEQcXKKj5YfDTIkNPcBBNyLrCv7M5V4LiwFh1n8VJy6H5RJrBKew"}, {"name":"Daffy Duck", "id":548001360, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ7leBgakzSpfs50MeDnkd39Lu_nZpyHIE_0tm3FzovjaGhiF7K"}]}]}
So to parse everything properly and without any problems, I’ve found such a structure in the net, which is able to take the necessary data from the container by the given key.
struct DecodingHelper { /// Dynamic key private struct Key: CodingKey { let stringValue: String init?(stringValue: String){ self.stringValue = stringValue self.intValue =nil} let intValue: Int? init?(intValue: Int){returnnil}} /// Dummy model that handles model extracting logic from a key private struct ContainerResponse<NestedModel: Decodable>: Decodable { let nested: NestedModel public init(from decoder: Decoder) throws { let key = Key(stringValue: decoder.userInfo[CodingUserInfoKey(rawValue:"key")!]! as! String)! let values = try decoder.container(keyedBy: Key.self) nested = try values.decode(NestedModel.self, forKey: key)}} static func decode<T: Decodable>(modelType: T.Type, fromKey key: String, data: Data) throws -> T { let decoder = JSONDecoder() // ***Pass in our key through `userInfo` decoder.userInfo[CodingUserInfoKey(rawValue:"key")!]= key let model = try decoder.decode(ContainerResponse<T>.self, from: data).nested return model }}
It’s rather convenient to work with such a structure.
let craracterValues = try DecodingHelper.decode(modelType:[Character].self, fromKey:"results", data: jsonData)
We have a static method, which allows me to make such a request and to set all the parameters: the type of the model I need to return, the key under which the model lies, and, obviously, the very data I need. Let’s look at the JSON we have. Each nesting level should be represented as a separate container with a data corresponding to a certain key.
Any data can be stored in the container, a massive with dictionaries, for instance, as well as a single data value like Int.
For example, to deparse friends in such an object:
{"name":"Porky Pig", "id":819263082, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTEQcXKKj5YfDTIkNPcBBNyLrCv7M5V4LiwFh1n8VJy6H5RJrBKew", "friends":[{"name":"Bugs Bunny", "id":825382838, "avatarPath":"https://avatarfiles.alphacoders.com/812/81220.webp"}, {"name":"Daffy Duck", "id":548001360, "avatarPath":"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ7leBgakzSpfs50MeDnkd39Lu_nZpyHIE_0tm3FzovjaGhiF7K"}]}
We need to take
let friendsContainer = try? generalContainer.nestedUnkeyedContainer(forKey: .friends)
container by the key .friends. from the main container child.
In fact, our new container will be a massive of objects and to deparse the objects we need to loop through it.
if var friendsContainer = friendsContainer { var friends:[Character]=[] while!friendsContainer.isAtEnd {if let friend = try friendsContainer.decodeIfPresent(Character.self){ friends.append(friend)}} self.friends =NSSet(array: friends)}
friendsContainer has the following property.isAtEnd. So we, in fact, iterate and create objects until the property returns “true”, which will mean the end of our massive.
Conclusion
Decodable protocol — is an extremely powerful tool, which is given to us for free by Apple. Thanks to it! The only thing you should do is study it, otherwise, you won’t be able to parse certain multi-level JSON objects.
Dear colleagues, I wish you luck and hope the article was useful for you.