Decoding a JSON without keys in Swift 4
If the structure stays the same, you can use this Decodable approach.
First create a decodable Model like this:
struct MyModel: Decodable { let firstString: String let stringArray: [String] init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() firstString = try container.decode(String.self) stringArray = try container.decode([String].self) }}
Or if you really want to keep the JSON's structure, like this:
struct MyModel: Decodable { let array: [Any] init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() let firstString = try container.decode(String.self) let stringArray = try container.decode([String].self) array = [firstString, stringArray] }}
And use it like this
let jsonString = """["A string1", ["A string2", "A string3", "A string4", "A string5"]]"""if let jsonData = jsonString.data(using: .utf8) { let myModel = try? JSONDecoder().decode(MyModel.self, from: jsonData)}
This is a bit interesting for decoding.
You don't have any key
. So it eliminates the need of a wrapper struct
.
But look at the inner types. You get mixture of String
and [String]
types. So you need something that deals with this mixture type. You would need an enum
to be precise.
// I've provided the Encodable & Decodable both with Codable for clarity. You obviously can omit the implementation for Encodableenum StringOrArrayType: Codable { case string(String) case array([String]) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() do { self = try .string(container.decode(String.self)) } catch DecodingError.typeMismatch { do { self = try .array(container.decode([String].self)) } catch DecodingError.typeMismatch { throw DecodingError.typeMismatch(StringOrArrayType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload conflicts with expected type")) } } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let string): try container.encode(string) case .array(let array): try container.encode(array) } }}
Decoding Process:
let json = """[ "A string", [ "A string", "A string", "A string", "A string" ]]""".data(using: .utf8)!do { let response = try JSONDecoder().decode([StringOrArrayType].self, from: json) // Here, you have your Array print(response) // ["A string", ["A string", "A string", "A string", "A string"]] // If you want to get elements from this Array, you might do something like below response.forEach({ (element) in if case .string(let string) = element { print(string) // "A string" } if case .array(let array) = element { print(array) // ["A string", "A string", "A string", "A string"] } })} catch { print(error)}
A possible solution is to use the JSONSerialization
, then you might simply dig inside such json, doing so:
import Foundationlet jsonString = "[\"A string\",[\"A string\",\"A string\", \"A string\", \"A string\"]]"if let jsonData = jsonString.data(using: .utf8) { if let jsonArray = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] { jsonArray.forEach { if let innerArray = $0 as? [Any] { print(innerArray) // this is the stuff you need } } }}