6

I'm trying to implement a Decodable to parse a json request but the json request has a dictionary inside of the object.

Here is my code:

    struct myStruct : Decodable {
        let content: [String: Any]
}

        enum CodingKeys: String, CodingKey {
            case content = "content"
}

But I'm getting this error:

Type 'MyClass.myStruct' does not conform to protocol 'Decodable'

How can declare a variable as dictionary without this error?

I'll really appreciate your help

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
user2924482
  • 8,380
  • 23
  • 89
  • 173
  • A dictionary is not going to be decodable. Maybe post the JSON. – picciano Apr 25 '18 at 19:17
  • `Any` does not conform to `Decodable`, so `Dictionary` doesn't either. If you actually need to store a dictionary with completely dynamic keys/values and hence cannot use concrete types rather than `Any`, then you need to stick to using `JSONSerialization`. If you do know all keys and the expected types of their values, then store the values as properties of your class with concrete types. – Dávid Pásztor Apr 25 '18 at 19:27
  • @user2924482 Take a crack at my [answer below](https://stackoverflow.com/a/50030133/2857130). Best of both: `Decodable` + `[String:Any]` – staticVoidMan Apr 25 '18 at 20:10

2 Answers2

6

Well... technically you could do this but it will require you to use a third party component SwiftyJSON for the dictionary representation.

Also, I am assuming you're doing this because content might have non-normalized data and that you intentionally want to treat it as a dictionary.

In that case, go ahead with this:

import SwiftyJSON

struct MyStruct : Decodable {
    //... your other Decodable objects like
    var name: String

    //the [String:Any] object
    var content: JSON
}

Here, JSON is the SwiftyJSON object that will stand in for your dictionary. Infact it would stand in for an array too.


Working Example:

let jsonData = """
{
  "name": "Swifty",
  "content": {
    "id": 1,
    "color": "blue",
    "status": true,
    "details": {
        "array" : [1,2,3],
        "color" : "red"
    }
  }
}
""".data(using: .utf8)!

do {
    let test = try JSONDecoder().decode(MyStruct.self,
                                        from: jsonData)
    print(test)
}
catch {
    print(error)
}
staticVoidMan
  • 19,275
  • 6
  • 69
  • 98
5

You cannot currently decode a [String: Any] with the Swift coding framework. You'll need to drop to a lower-level deserialization strategy and decode “by hand” if you need to decode a [String: Any]. For example, if your input is JSON, you can use Foundation's JSONSerialization or a third-party library like SwiftyJSON.

There has been discussion of this issue on Swift Evolution: “Decode a JSON object of unknown format into a Dictionary with Decodable in Swift 4”. Apple's main Coding/Codable programmer, Itai Ferber, has been involved in the discussion and is interested in providing a solution, but it is unlikely to happen for Swift 5 (which will probably be announced at WWDC 2018 and finalized around September/October 2018).

You could copy the implementation of JSONDecoder (it's open source) into your project and modify it to add the ability to get an unevaluated [String: Any]. Itai discusses the required modifications in the thread I linked above.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848