1

I use the new Codable protocol to turn a struct into a JSON and then into a dictionary for testing purposes. The problem is that the dictionary variable within the struct doesn't get converted back and stays Any rather than a [Int: String]

struct Person: Codable {
    var name: String?
    var history: [Int: String]

    init() {
        self.name = "Name"
        history = [0: "Test"]
    }
}

let person = Person()

let jsonData = try JSONEncoder().encode(person)

let result = try JSONSerialization.jsonObject(with: jsonData, options: [])

let dictionary = result as? [String: Any]

print(dictionary)

This gives me the following result

Optional(["history": {
    0 = Test;
}, "name": Name])

When I would expect

Optional(["history":[0: "Test"]], "name": "Test"])

I would appreciate any explanation as to why this happens or, better yet, a solution how to basically do deep JSON serialization.

I am adding a playground demonstrating the problem: https://www.dropbox.com/s/igpntk7az0hevze/JSONSerialisation.playground.zip

3
  • Why would you expect it to look like that? The Dictionary you are printing is full blown dictionary and the data appears to be exactly what it's supposed to be. Keep in mind that [Int: String] is not an array, it's a dictionary and {0 = Test;} is how Swift formats a dictionary when you print it to the console for debugging... To me it seems like your code is working fine. Commented Jul 19, 2017 at 15:02
  • 1
    Hi donnywals, actually that's not how swift prints a dictionary. If I define a dictionary as let sampleDictionary: [String: Any] = ["history":[0: "Test"], "name": "Test"] and then print it print(sampleDictionary), the result is ["history": [0: "Test"], "name": "Test"]. And if I try to access history by using let history = dictionary["history"] as? [Int: String], the value is nil because it is not a dictionary. Commented Jul 19, 2017 at 16:29
  • Whoops.. you seem to be absolutely right Denis! Commented Jul 19, 2017 at 21:55

2 Answers 2

1

As you used JSONEncoder for encoding, you can also make use of JSONDecoder to decode the json data. In this case, history is a dictionary which is not one of the default types, so adding init(from decoder: Decoder) with customisations would be a work around to get the expected dictionary.

struct Person: Codable {
    var name: String
    var history: [Int: String]

    init() {
        self.name = "Name"
        history = [0: "Test"]
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Failed to decode name"
        self.history = try container.decodeIfPresent([Int: String].self, forKey: .history) ?? [-1: "Failed to decode history"]
    }
}

let person = Person()
do {
    let jsonData = try JSONEncoder().encode(person)

    if let result = try? JSONDecoder().decode(Person.self, from: jsonData) {
        print(result)
    }
} catch {
    print(error.localizedDescription)    
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Lawliet, you are quite correct that in that I could decode it into a person, I don't even need to override the init from decoder because actually, the dictionary is parsed just fine.
0

Turns out it is a dictionary, it just prints as if it wasn't one. The reason why I believed it was not a dictionary was that calling

let history = dictionary["history"] as? [Int: String] 

would lead to a nil, but the cause was that JSON apparently doesn't handle [Int: String]. Instead, my [Int: String] has been turned (probably by the JSONSerialization.jsonObject(with: jsonData, options: []) function) into a [String: Any].

let history = dictionary["history"] as? [String: String]

works just fine.

I still don't understand why the console prints like with the curly brackets though, maybe a bug?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.