-1

I have this JSON save in file ("list_catalog.json"):

[
 {
  "idcatalog": 1,
  "imgbase64": "",
  "urlImg": "http://172.../page01.jpg",
  "urlPages": "http://172.../catalog_1_pages.json",
  "dt_from": "",
  "dt_to": ""
 },
 {
  "idcatalog": 2,
  "imgbase64": "",
  "urlImg": "http://172.../1.jpg",
  "urlPages": "http://172.../2_pages.json",
  "category": [
   {
    "id": 1,
    "lib": "lib"
   }
  ],
  "dt_to": ""
 }
]

I can get this file with :

if let url = URL(string: "http://172.../list_catalogs.json") {
        do {
            let contents = try String(contentsOf: url)
            print(contents)

        } catch {
            // contents could not be loaded
        }
    }

But I can't convert this String in dictionary. For convert data to string I use this function :

func convertToDictionary(text: String) -> [String: Any]? {
    if let data = text.data(using: .utf8) {
        do {
            return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
        } catch {
            print(error.localizedDescription)
        }
    }
    return nil
}

But for this situation it doesn't work Somebody can help me ?

John Puagnol
  • 3
  • 1
  • 2
  • 3
    Welcome to StackOverflow. Please show what the error output is. Also if your using Swift 4 it's better to use the `Codable` protocol and `JSONDecoder()` – Scriptable Sep 13 '18 at 09:41
  • Instead of dictionary, convert it to struct using Codable. See https://www.raywenderlich.com/382-encoding-decoding-and-serialization-in-swift-4 – regina_fallangi Sep 13 '18 at 09:42
  • 2
    You cannot convert it to a dictionary because it's not a dictionary. It's an array (note the `[]`). Cast it to `[[String:Any]]`. And since Swift 3 the `options` parameter is optional. You can omit it. – vadian Sep 13 '18 at 09:49
  • 2
    Be aware that, as explained in the [original answer](https://stackoverflow.com/a/30480777/2227743), this `convertToDictionary` method is just a *convenience* method to use **only** if you have to work with a JSON *string*. But most of the times you will get JSON *data*, so you won't need this method at all. – Eric Aya Sep 13 '18 at 09:49
  • Is this correct `"urlImg": "http://172.../page01.jpg"`, or are you just hiding the ip address? – ielyamani Sep 13 '18 at 09:57
  • @Carpsen90 I hiding the ip. – John Puagnol Sep 13 '18 at 10:10
  • @vadian Thx for this solution – John Puagnol Sep 13 '18 at 10:25

1 Answers1

6

The problem with the code you have is that you are trying to convert an array to a dictionary (as rightly pointed out by a couple of people in the comments Vadian, Moritz at the time of writing).

So the obvious, first step solution would be instead of casting to [String: Any] cast to [[String: Any]] (an array of dictionaries) but this then still leaves you with the problem of the Any part. To work with this dictionary you need to know/remember the keys and their types and cast each value as you use it.

It is much better to use the Codable protocol which along with the decoder allows you to basically map the JSON to related code structures.

Here is an example of how you could parse this JSON using the Swift 4 codable protocol (done in a Swift Playground)

let jsonData = """
[{
    "idcatalog": 1,
    "imgbase64": "",
    "urlImg": "http://172.../page01.jpg",
    "urlPages": "http://172.../catalog_1_pages.json",
    "dt_from": "",
    "dt_to": ""
}, {
    "idcatalog": 2,
    "imgbase64": "",
    "urlImg": "http://172.../1.jpg",
    "urlPages": "http://172.../2_pages.json",
    "category": [{
        "id": 1,
        "lib": "lib"
    }],
"dt_to": ""
}]
""".data(using: .utf8)!

struct CatalogItem: Codable {
    let idCatalog: Int
    let imgBase64: String?
    let urlImg: URL
    let urlPages: URL
    let dtFrom: String?
    let dtTo: String?
    let category: [Category]?

    enum CodingKeys: String, CodingKey {
        case idCatalog = "idcatalog"
        case imgBase64 = "imgbase64"
        case urlImg, urlPages, category
        case dtFrom = "dt_from"
        case dtTo = "dt_to"
    }
}

struct Category: Codable {
    let id: Int
    let lib: String?

    enum CodingKeys: String, CodingKey {
        case id, lib
    }
}

do {
    let decoder = JSONDecoder()
    let result = try decoder.decode([CatalogItem].self, from: jsonData)
    print(result)
} catch {
    print(error)
}

Console output:

[__lldb_expr_118.CatalogItem(idCatalog: 1, imgBase64: Optional(""), urlImg: http://172.../page01.jpg, urlPages: http://172.../catalog_1_pages.json, dtFrom: Optional(""), dtTo: Optional(""), category: nil), __lldb_expr_118.CatalogItem(idCatalog: 2, imgBase64: Optional(""), urlImg: http://172.../1.jpg, urlPages: http://172.../2_pages.json, dtFrom: nil, dtTo: Optional(""), category: Optional([__lldb_expr_118.Category(id: 1, lib: Optional("lib"))]))]

Now comes the good part, it's much easier to use this data now...

print(result.first?.urlImg)

Output:

Optional(http://172.../page01.jpg)

This works for the example JSON you provided but may need more tweaks based on the rest of your data set.

Scriptable
  • 19,402
  • 5
  • 56
  • 72