Home > Blockchain >  Trying to decode dynamic JSON response
Trying to decode dynamic JSON response

Time:05-08

I'm trying to decode the JSON response for all tags from the Pinboard API. I have a simple model I'd like to decode into:

    struct Tag: Coddle, Hashable {
        let name: String
        let count: Int
    }

The problem is that the JSON response I get is entirely dynamic, like this:

    {
        "tag_name_1": 5,
        "tag_name_2": 5,
        "tag_name_3": 5,
    }

So decoding into my model using JSONDecoder.decode([Tag].self, data) always fails.

CodePudding user response:

Your JSON can be decoded into a dictionary of type [String: Int].

If you enter key and value of each entry of the dictionary into a Tag object, you can then sort the array of tags by name. Or you can sort the dictionary keys first, both work.

Catch: it will work only if the order of the JSON is also the order of the keys. It will NOT follow the order if the JSON arrives like this, for example:

{
        "tag_name_3": 5,
        "tag_name_2": 5,
        "tag_name_1": 5,
}

Here's the code (with two options):

    // First way:
    // 1) Decode the dictionary
    // 2) Sort the dictionary keys
    // 3) Iterate, creating a new Tag and appending it to the array
    private func decode1() {
        let json = "{ \"tag_name_1\": 5, \"tag_name_2\": 5, \"tag_name_3\": 5}"
        let data = json.data(using: .utf8)

        do {
            
            // Decode into a dictionary
            let decoded = try JSONDecoder().decode([String: Int].self, from: data!)
            print(decoded.sorted { $0.key < $1.key })
            
            var tags = [Tag]()
            
            // Iterate over a sorted array of keys
            decoded.compactMap { $0.key }.sorted().forEach {
                
                // Create the Tag
                tags.append(Tag(name: $0, count: decoded[$0] ?? 0))
            }
            print(tags)
        } catch {
            print("Oops: something went wrong while decoding, \(error.localizedDescription)")
        }
    }
    // Second way:
    // 1) Decode the dictionary
    // 2) Create the Tag objects
    // 3) Sort the array by Tag.name
    private func decode2() {
        let json = "{ \"tag_name_1\": 5, \"tag_name_2\": 5, \"tag_name_3\": 5}"
        let data = json.data(using: .utf8)

        do {

            // Decode into a dictionary
            let decoded = try JSONDecoder().decode([String: Int].self, from: data!)
            print(decoded.sorted { $0.key < $1.key })
            
            var tags = [Tag]()

            // Iterate over the dictionary entries
            decoded.forEach {
                
                // Create the Tag
                tags.append(Tag(name: $0.key, count: $0.value))
            }
            
            // Sort the array by name
            tags = tags.sorted { $0.name < $1.name }
            print(tags)
        } catch {
            print("Oops: something went wrong while decoding, \(error.localizedDescription)")
        }
    }
  • Related