Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fatal error: Dictionary<String, Any> does not conform to Decodable because Any does not conform to Decodable

I'm trying to use swift 4 to parse a local json file:

{
    "success": true,
    "lastId": null,
    "hasMore": false,
    "foundEndpoint": "https://endpoint",
    "error": null
}

This is the function I'm using:

    func loadLocalJSON() {

        if let path = Bundle.main.path(forResource: "localJSON", ofType: "json") {
            let url = URL(fileURLWithPath: path)

            do {
                let data  = try Data(contentsOf: url)
                let colors = try JSONDecoder().decode([String: Any].self, from: data)
                print(colors)
            }
            catch { print("Local JSON not loaded")}
        }
    }
}

but I keep getting the error:

Fatal error: Dictionary does not conform to Decodable because Any does not conform to Decodable.

I tried using the "AnyDecodable" approach on this stackoverflow page: How to decode a property with type of JSON dictionary in Swift 4 decodable protocol but it jumps to the 'catch' statement: catch { print("Local JSON not loaded") when being used. Does anyone know how to parse this JSON data in Swift 4?

like image 796
SwiftyJD Avatar asked Feb 27 '18 21:02

SwiftyJD


2 Answers

Maybe you are misunderstanding how Codable works. It's based on concrete types. Any is not supported.

In your case you might create a struct like

struct Something: Decodable {
    let success : Bool
    let lastId : Int?
    let hasMore: Bool
    let foundEndpoint: URL
    let error: String?
}

And decode the JSON

func loadLocalJSON() {
    let url = Bundle.main.url(forResource: "localJSON", withExtension: "json")!
    let data  = try! Data(contentsOf: url)
    let colors = try! JSONDecoder().decode(Something.self, from: data)
    print(colors)
}

Any crash will reveal a design error. The sense of using null in a file in the main bundle is another question.

like image 50
vadian Avatar answered Nov 01 '22 12:11

vadian


I used quicktype to generate Codables and marshaling code:

https://app.quicktype.io?gist=02c8b82add3ced7bb419f01d3a94019f&l=swift

I gave it an array of samples based on your sample data:

[
  {
    "success": true,
    "lastId": null,
    "hasMore": false,
    "foundEndpoint": "https://endpoint",
    "error": null
  },
  {
    "success": true,
    "lastId": 123,
    "hasMore": false,
    "foundEndpoint": "https://endpoint",
    "error": "some error"
  }
]

This tells quicktype to assume that the null values in your first sample are sometimes Int and String – you can change these if they aren't the possible types. The resulting Codable generated is:

struct Local: Codable {
    let success: Bool
    let lastID: Int?
    let hasMore: Bool
    let foundEndpoint: String
    let error: String?

    enum CodingKeys: String, CodingKey {
        case success
        case lastID = "lastId"
        case hasMore, foundEndpoint, error
    }
}
like image 24
David Siegel Avatar answered Nov 01 '22 10:11

David Siegel