I'm using an API that returns this pretty horrible JSON:
[ "A string", [ "A string", "A string", "A string", "A string", … ] ]
I'm trying to decode the nested array using JSONDecoder, but it doesn't have a single key and I really don't know where to start… Do you have any idea?
Thanks a lot!
You just have to use json_decode() function to convert JSON objects to the appropriate PHP data type. Example: By default the json_decode() function returns an object. You can optionally specify a second parameter that accepts a boolean value. When it is set as “true”, JSON objects are decoded into associative arrays.
An object that converts between JSON and the equivalent Foundation objects.
Swift JSON ParsingJSON stands for JavaScript Object Notation. It's a popular text-based data format used everywhere for representing structured data. Almost every programming language supports it with Swift being no exception. You are going to use JSON a lot throughout your career, so make sure you don't miss out.
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 Encodable enum 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) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With