Using Codable with Nested JSON? You Can!

Nesting? Don’t worry!

Image for post
Image for post
Photo by Rana Sawalha on Unsplash

Still using several structs to decode data that is in a nested JSON String? Well, no longer! This article is here to help you out.

Do note that following this article involves testing from the Playground and using Codable

Difficulty: Beginner | Easy | Normal | Challenging

The JSON String

The following JSON response is meant to represent a user. Now this is a common construct, and as we can see has a nested name within the jsonString.

Image for post
Image for post
Click for Gist

The poor solution

A common (but poor) solution for this is to produce objects of type struct and decode. The separate objects are…not ideal:

Image for post
Image for post
Click for Gist

This can then be tested (of course if you are following along in a Playground you’ll need to import XCTest and also JSONTests.defaultTestSuite.run())

There’s a further problem: User doesn’t conform to Equatable so I’ll whack this into an extension.

Image for post
Image for post
Click for Gist

What do you know?

A Fully passing test. But did you know that things can be more awesome?

The awesome solution

The Model will be changed (slightly) in name to make this easier to read on a single Playground so is called UserModel for this section.

This is slightly more tricky than the approach above, as the CodingKeys need to be specified individually.

In this example we have four variables (gender, title, first and last) with all of them containing Strings.

The top-level coding keys will be Gender (because it is, as expected at the top-level of the JSON String). The object name is also defined as one of the case statements (despite not having a variable to match). The next-level of coding keys are the next level keys from the JSON string (title, first and last).

This gives us a start for the UserModel:

Image for post
Image for post
Click for Gist

Now we need to conform to the Decodable and the Encodable protocols.

Decodable

As far as Decodable is concerned, we provide a custom initializer. The Top-level coding keys har handled first.

The easiest is gender which simply operates through a top-level container let container = try decoder.container(keyedBy: CodingKeys.self), and gender is created through gender = try container.decode(String.self, forKey: .gender) which works through the enum stated above (and of course is decoded as a String).

We know that name is also at the top level, but has a key from the name let name = try container.nestedContainer(keyedBy: NameKeys.self, forKey: .name) — but effectively this is just a second container that then enables us to populate the second-tier variables:

first = try name.decode(String.self, forKey: .first)
last = try name.decode(String.self, forKey: .last)
title = try name.decode(String.self, forKey: .title)

As a whole this is coded as:

Image for post
Image for post
Click for Gist

Encodable

For Encodable we once define the top level and second-level variables should be encoded. Again name is effectively a second container (the type of name is actually KeyedEncodingContainer).

The code for this is given below:

Image for post
Image for post
Click for Gist

Testing

Testing this is known as a very tricky process. But don’t worry; I’ve got you covered. But in order to do so we need to compare UserModel instances; to do so we conform to the Equatable protocol (with empty brackets as this already conforms):

Image for post
Image for post
Click for Gist

The we create a test that encodes and decodes the string (an unchanged string from the start of this article). The code for this is given below:

Image for post
Image for post
Click for Gist

The full gist

Conclusion

It actually is quite tricky to deal with nested JSON statements. Actually that isn’t true, doing it in the less good way is easy. Producing multiple structs to simply deal with nested JSON isn’t on though, and perhaps shouldn’t pass that code review over at your place.

Indeed: I hope this article does help you out in creating great software that is both great for your user and even great for that code review by using better techniques.

If you’ve got questions, please do get in touch over on Twitter and I’ll do everything I can to help you out.

Extend your knowledge

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store