3

I have some JSON that comes back in the following format,

{
"Random Word": [
    [
        "2017-08-10",
        6
    ],
    [
        "2017-08-11",
        6
    ],
    [
        "2017-08-15",
        4
    ]
],
"Another Random Word": [
    [
        "2017-08-10",
        4
    ],
    [
        "2017-08-11",
        4
    ],
    [
        "2017-08-12",
        1
    ],
    [
        "2017-08-14",
        2
    ],
    [
        "2017-08-15",
        4
    ],
    [
        "2017-08-16",
        1
    ]
]
}

The issue is that the 'key' will be different each time and the 'value' contains a heterogeneous array of Strings (that should be converted to Dates), and Ints.

Is there a way to use Swift's Decodable protocol to turn this into objects?

Here is a Struct that it could be decoded as,

struct MyJSONData: Decodable {

    var myInfo: Dictionary<String, [[Any]]>?
    ...
}

However, if there is a better way to structure the struct, Im all ears!

Thanks in advance.

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
cheaze
  • 187
  • 1
  • 14

2 Answers2

1

I'm fairly certain your case is similar to a question I recently posted: Flattening JSON when keys are known only at runtime.

If so, you could use the following solution:

struct MyJSONData: Decodable {
    var dates = [Any]()

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()

        // Only use first item
        let stringItem = try container.decode(String.self)
        dates.append(stringItem)
        let numberItem = try container.decode(Int.self)
        dates.append(numberItem)
    }
}

let decoded = try! JSONDecoder().decode([String : [MyJSONData]].self, from: jsonData).values
// Returns an Array of MyJSONData

Working solution: http://swift.sandbox.bluemix.net/#/repl/59949d74677f2b7ec84046c8

nathan
  • 9,329
  • 4
  • 37
  • 51
1

I was working with an API which encoded JSON array with heterogeneous data like yours, but even the order of the columns was not know before hand :(

In general, I strongly recommend against storing data in heterogeneous arrays. You will very quickly forget what index stands for what property, not to mention the constant cast back and forth. Instead, make a data model to store it when you decode from the array.

Another thing to note is that your date isn't what JSONDecoder expects by default. It expects ISO 8601 format (yyyy-MM-ddTHH:mm:ssZ) whereas the time component is missing from your date string. You can tell JSONDecoder what to do by supplying a custom DateFormatter:

struct WordData: Decodable {
    var date: Date
    var anInt: Int

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        self.date = try container.decode(Date.self)
        self.anInt = try container.decode(Int.self)
    }
}

var dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_us_POSIX")
dateFormatter.timeZone = TimeZone(identifier: "UTC")
dateFormatter.dateFormat = "yyyy-MM-dd"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)

let words = try decoder.decode([String: [WordData]].self, from: jsonData)
Code Different
  • 90,614
  • 16
  • 144
  • 163