How to Parse Array of JSON to Array in Swift

How to parse Array of JSON to array in Swift

 var peoplesArray:[Any] = [
[
"People": [
"Jack",
"Jones",
"Rock",
"Taylor",
"Rob"
]
],
[
"People": [
"Rose",
"John"

]
],
[
"People": [
"Ted"
]
]
]

var finalArray:[Any] = []

for peopleDict in peoplesArray {
if let dict = peopleDict as? [String: Any], let peopleArray = dict["People"] as? [String] {
finalArray.append(peopleArray)
}
}

print(finalArray)

output:

[["Jack", "Jones", "Rock", "Taylor", "Rob"], ["Rose", "John"], ["Ted"]]

In your case, it will be:

if let path = Bundle.main.path(forResource: "People", ofType: "json") {
let peoplesArray = try! JSONSerialization.jsonObject(with: Data(contentsOf: URL(fileURLWithPath: path)), options: JSONSerialization.ReadingOptions()) as? [Any]

var finalArray:[Any] = []

for peopleDict in peoplesArray {
if let dict = peopleDict as? [String: Any], let peopleArray = dict["People"] as? [String] {
finalArray.append(peopleArray)
}
}

print(finalArray)
}

Parsing JSON array in Swift with array root object

The issue there is that you are trying to decode a dictionary instead of an array.

Just change

let postSet = try decoder.decode(DataSet.self, from: Data(dataString.utf8))

to

let postSet = try decoder.decode([SinglePost].self, from: Data(dataString.utf8))

edit/update:

Regarding your method signature just change it to return an array instead of a DataSet structure and I would return the error instead of a Bool:

static func getPost(completion: @escaping ([SinglePost]?, Error?) -> ()) {
let session = URLSession.shared
let decoder = JSONDecoder()
if let url = URL(string: serverUrl + "/post/") {
let task = session.dataTask(with: url) { data, response, error in
guard let data = data else {
completion(nil, error)
return
}
do {
completion(try decoder.decode([SinglePost].self, from: data), nil)
} catch {
print("Error Decoding JSON", error)
completion(nil, error)
}
}
task.resume()
}
}

Usage:

getPost { posts, error in
guard let posts = posts else {
print(error ?? "")
}
for post in posts {
print(post)
}
}

How to parse json in swift 4 an array inside an array

The value for key StatusList is a dictionary, please note the {}, the array is the value for key Details in statusList

if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
let statusList = parsedData["StatusList"] as? [String:Any],
let details = statusList["Details"] as? [[String:Any]] {
for detail in details {
print(detail["Pro"])
}
}
}

And don't do things like (... as? ...)!, never!

The corresponding Codable structs are

struct Status: Codable {
let mobileAPIError: String
let statusList: StatusList

enum CodingKeys: String, CodingKey { case mobileAPIError = "MobileAPIError", statusList = "StatusList" }
}

struct StatusList: Codable {
let errorMessage: String
let details: [Detail]

enum CodingKeys: String, CodingKey { case errorMessage = "ErrorMessage", details = "Details" }
}

struct Detail: Codable {
let pro, blNumber, referenceNumber, scac: String
let carrier, shipperCode, shipperName, shipperCity: String
let shipperState: String

enum CodingKeys: String, CodingKey {
case pro = "Pro", blNumber = "BlNumber", referenceNumber = "ReferenceNumber"
case scac = "Scac", carrier = "Carrier", shipperCode = "ShipperCode"
case shipperName = "ShipperName", shipperCity = "ShipperCity", shipperState = "ShipperState"
}
}

do {
let result = try JSONDecoder().decode(Status.self, from: data!)
print(result)
} catch { print(error) }

How to convert JSON array objects into list array | Swift 5

First of all please name structs and classes always with starting uppercase letter and declare the struct members non-optional if the API sends consistent data

struct TestStructure: Decodable {
let name: String
let orderid: Int
let trialid: Int
}

Second of all the decoding line doesn't compile, you have to write

let nav = try JSONDecoder().decode([TestStructure].self, from: data)

To get an array of all trialid values just map it

let allTrialids = nav.map(\.trialid)

Update: Take the compiler's advice and add explicit type to disambiguate

self.viewControllers = nav.map { test -> UIViewController in
let selected = UIImage(named: "Tab4_Large")!
let normal = UIImage(named: "Tab4_Large")!
let controller = storyboard!.instantiateViewController(withIdentifier: String(test.trialid))
controller.view.backgroundColor = UIColor.white
controller.floatingTabItem = FloatingTabItem(selectedImage: selected, normalImage: normal)
return controller
}

Decode json array as an object in swift

First, I assume you'd really like the final result to be [Product] where Product looks like this:

struct Product {
var name: String = ""
var quantity: Int = 0
}

So if any keys are missing, they'll get the defaults. You could change this solution to throw an error in that case, but I think this approach is much nicer than Optionals.

Given that, here's how you decode it:

extension Product: Decodable {
init(from decoder: Decoder) throws {
// This is a very uniform type (all Strings). Swift can decode that
// without much help.
let container = try decoder
.singleValueContainer()
.decode([String: [[String: String]]].self)

// Extract the Products. Throwing an error here might be nicer.
let keyValues = container["Product"] ?? []

// This loops over every Key-Value element, and then loops through
// each one. We only expect one element in the second loop, though.
for keyValue in keyValues {
for (key, value) in keyValue {
switch key {
case "Name": self.name = value
case "Quantity": self.quantity = Int(value) ?? 0
default: break // Ignore unknown keys
}
}
}
// This solution just assigns defaults for missing keys, but
// you could validate that everything was found, and throw an
// error here if desired.
}
}

let data = try JSONDecoder().decode([Product].self, from: jsonData)
// [{name "exampleName", quantity 1}]

data.first!.name
// "exampleName"

In most cases the above is probably fine, but it's also very sloppy about malformed data. It just returns a default object, which could make an error very hard to track down. This example goes in the other direction, and does all the error checking you would expect from a normal Decodable.

First, we'll need a CodingKey that can accept any String. I really don't understand why this isn't built into stdlib:

struct AnyStringKey: CodingKey, Hashable, ExpressibleByStringLiteral {
var stringValue: String
init(stringValue: String) { self.stringValue = stringValue }
init(_ stringValue: String) { self.init(stringValue: stringValue) }
var intValue: Int?
init?(intValue: Int) { return nil }
init(stringLiteral value: String) { self.init(value) }
}

I also find DecodingErrors very cumbersome to build, so I often build little helper functions:

func keyNotFound(_ key: String, codingPath: [CodingKey]) -> Error {
DecodingError.keyNotFound(AnyStringKey(key),
.init(codingPath: [],
debugDescription: "\(key) key not found"))
}

func typeMismatch(_ key: String, expected: Any.Type, codingPath: [CodingKey]) -> Error {
DecodingError.typeMismatch(expected, .init(codingPath: codingPath + [AnyStringKey(key)],
debugDescription: "Expected \(expected)."))
}

With those in place, here's a more strict decoder:

extension Product: Decodable {
init(from decoder: Decoder) throws {
// This is a very uniform type (all Strings). Swift can decode that
// without much help.
let container = try decoder
.singleValueContainer()
.decode([String: [[String: String]]].self)

var codingPath: [AnyStringKey] = []

// Extract the Products. Throwing an error here might be nicer.
guard let keyValues = container["Product"] else {
throw keyNotFound("Product", codingPath: codingPath)
}

codingPath.append("Product")

var name: String?
var quantity: Int?

// This loops over every Key-Value element, and then loops through
// each one. We only expect one element in the second loop, though.
for keyValue in keyValues {
for (key, value) in keyValue {
switch key {
case "Name":
name = value

case "Quantity":
guard let intValue = Int(value) else {
throw typeMismatch("Quantity",
expected: Int.self,
codingPath: codingPath)
}
quantity = intValue

default: break // Ignore unknown keys
}
}
}

guard let name = name else {
throw keyNotFound("Name", codingPath: codingPath)
}
self.name = name

guard let quantity = quantity else {
throw keyNotFound("Quantity", codingPath: codingPath)
}
self.quantity = quantity
}
}

How to parse array of strings in swift?

If you're getting the 3840 error, then the JSON is not valid JSON, period.

The JSON string equivalent of the array is supposed to be

let jsonString = "[\"one\",\"two\",\"three\"]"

The Swift 4 literal multiline syntax shows the actual format without the escaping backslashes

let jsonString = """
["one","two","three"]
"""

You are able to parse it without any options (no .allowFragments, and no .mutableContainers)

let data = Data(jsonString.utf8)
do {
let array = try JSONSerialization.jsonObject(with: data) as! [String]
print(array) // ["one", "two", "three"]
} catch {
print(error)
}

Almost everybody misuses the JSONSerialization Reading Options

  • .allowFragments is only needed if the root object is not array and not dictionary
  • .mutableContainers is completely meaningless in Swift

In 99% of the cases you can omit the options parameter

How can i parse an Json array of a list of different object using Codable?

A reasonable solution is an enum with associated values because the type can be determined by the productype key. The init method first decodes the productype with a CodingKey then in a switch it decodes (from a singleValueContainer) and associates the proper type/value to the corresponding case.

enum ProductType: String, Codable {
case a, b, c
}

struct Root : Codable {
let items : [Product]
}

struct ProductA : Codable {
let id, name: String
let productype: ProductType
let propertyOfA : String
}

struct ProductB : Codable {
let id, name: String
let productype: ProductType
let propertyOfB : String
}

struct ProductC : Codable {
let id, name: String
let productype: ProductType
let propertyOfC, propertyOfC2 : String
}

enum Product : Codable {

case a(ProductA), b(ProductB), c(ProductC)

enum CodingKeys : String, CodingKey { case productype }

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ProductType.self, forKey: .productype)
let singleContainer = try decoder.singleValueContainer()
switch type {
case .a : self = .a(try singleContainer.decode(ProductA.self))
case .b : self = .b(try singleContainer.decode(ProductB.self))
case .c : self = .c(try singleContainer.decode(ProductC.self))
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .a(let productA): try container.encode(productA)
case .b(let productB): try container.encode(productB)
case .c(let productC): try container.encode(productC)
}
}
}

And decode

let jsonString = """
{
"items": [
{
"id": "1",
"name": "name",
"propertyOfA": "1243",
"productype": "a"

},
{
"id": "2",
"name": "name",
"propertyOfA": "12",
"productype": "a"
},
{
"id": "3",
"name": "name",
"propertyOfA": "1243",
"productype": "a"
},
{
"id": "1",
"name": "name",
"propertyOfB": "1243",
"productype": "b"
},
{
"id": "1",
"name": "name",
"propertyOfC": "1243",
"propertyOfC2": "1243",
"productype": "c"
}
]
}
"""
do {
let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
print(result)
} catch { print(error)}

To read the enum values use also a switch.



Related Topics



Leave a reply



Submit