Can't Parse JSON Array with JSONdecoder in Swift 4

Can't parse JSON array with JSONDecoder in Swift 4

Remove the initial values for the constant properties

class Result: Codable{
let title: String
let id: Int
let key: String
let name: String

// ...
}

(or make the properties variable). Initialized constant
properties can not be set by the JSON decoder.

Can't Parse Array JSON Decoder Swift 4

You are saying decode([Root].self..., as if your JSON were an array of Root. But it is not an array of Root. It is a Root. Say decode(Root.self.

Then change

for info in articles

To

for info in articles.server_response

That is the array.

How to parse a JSON array of arrays and refer to the members of the inner array by names?

You can use your MoveModel but since each of the inner arrays represent one instance of MoveModel you need to change your first struct to

struct MovesResponse: Codable {
let moves: [MoveModel]
}

and then you need a custom init(from:) in MoveModel that decodes each array to a MoveModel object using an unkeyed container instead of coding keys.

init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
mine = try container.decode(Int.self)
words = try container.decode(String.self)
letters = try container.decode(String.self)
}

Rather than using try? and printing a hardcoded message I suggest you catch the error and print it

let decoder = JSONDecoder()
do {
let movesModel = try decoder.decode(MovesResponse.self, from: data)
print(movesModel)
} catch {
print(error)
}

JSONDecoder can't decode Array but can decode String

The fact that you can decode a String.self and get your JSON printed means that the root of the JSON is a string. The JSON you got, in text form, probably looks like this:

"[\r\n   {\"id\": 5913, \"subsidiary_ref\": \"0000159\", \"name\": \"Mercator Hipermarket Koper\", \"address\": \"Poslov.98-Koper,Dolinska C\", \"city\": \"Koper\", \"coordinates_x\": null, \"coordinates_y\": null, \"phone_number\": \"+386 56-636-800\", \"frequency\": \"A\", \"channel\": \"Food\", \"subchannel\": \"Hypermarket\", \"customer\": 7, \"day_planned\": true, \"badge_visible\": true, \"open_call\": true, \"number_of_planned\": 13, \"number_of_calls\": 22, \"last_call_day\": 3, \"notes\": \" notesi ki ne smejo nikoli zginiti bla marko bdsa\"}\r\n]"

Note that the actual JSON you want is put in ""s, so all the special characters are escaped.

The web service has given the JSON in a very weird way...

To get the actual JSON data decoded, you need to get the string first, then turn the string into Data, then decode that Data again:

// nil handling omitted for brevity
let jsonString = try! JSONDecoder().decode(String.self, from: data)
let jsonData = jsonString.data(using: .utf8)!
let sub = try! JSONDecoder().decode([Subsidiary].self, from: jsonData)

Of course, the best solution is to fix the backend.

Swift 4 json decode with top level array

There is no need to creat a custom initialiser. You just use the Array type [API].self when decoding your json:

struct API: Decodable{
let success: String
let message: String
}

let dataJSON = Data("""
[
{
"success": "true",
"message": "testtt"
}
]
""".utf8)

do {
if let result = try JSONDecoder().decode([API].self, from: dataJSON).first {
print(result.success)
print(result.message)
}
} catch {
print(error)
}

Swift Codable JSON parse error with JSONDecoder

You have several mistakes on defining object. For example you have defined id, msupply is a integer but they are string. You have defined the volume24 and volume24a is string but they are not. You need to fix all of these probably thats why you can't parse it.

All wrong defined parameters are id, mSupply, volume24, volume24a, tSupply for coin object and for the info object you need to convert time to Int aswell.

Parse JSON from mixed array with Decodable Swift

First, I'm assuming this is the data structure you want. If it's not, please update the question with your expected output data structure. (report[0][0].name is fragile and awkward to use in Swift. Create a struct that matches the data you would like to have. Then decode into that struct.)

struct ApiData: Decodable {
let report: [Details]
}

struct Details {
let name: String
let value1: Int
let value2: Int
}

So each Details has exactly a name object and two integer values. And a report is a list of Details.

given that, you can hand-decode Details using a unkeyedContainer. To get the name field out, it is handy to make a helper struct:

extension Details: Decodable {
struct NameField: Decodable {
let name: String
}

init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.name = try container.decode(NameField.self).name
self.value1 = try container.decode(Int.self)
self.value2 = try container.decode(Int.self)
}
}

Your comment notes "there is some more below name." In that case, you may want an object rather than a string here. In that case, it would look like this:

struct Person: Decodable {
let name: String
}

struct Details {
let person: Person
let value1: Int
let value2: Int
}

extension Details: Decodable {

init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.person = try container.decode(Person.self)
self.value1 = try container.decode(Int.self)
self.value2 = try container.decode(Int.self)
}
}

Finally, it's possible that the list of values is unbounded, and you want an array:

struct Details {
let person: Person
let values: [Int]
}

In that case you'd decode by extracting all the values out of the unkeyedContainer:

extension Details: Decodable {    
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
self.person = try container.decode(Person.self)
var values: [Int] = []
while !container.isAtEnd {
values.append(try container.decode(Int.self))
}
self.values = values
}
}

There are lots of ways to approach this. It all depends on how you want to use the data, and how much structure the data has.

Parse JSON Array that contains string and int in Swift 4


  1. Complicated way with Codable

    • Decode the array with unkeyedContainer
    • Use a while loop with condition !isAtEnd and decode Int in a do - catch block. If it fails decode String
  2. Easy way with traditional JSONSerialization

    • Deserialize the object to [CustomStringConvertible] and map the array to [String] with "\($0)"

Edit:

This is an example how to decode the array with Decodable if the items are pairs and the type order is the same:

let jsonArray = """
["A", 1, "A1", 13]
"""

struct Item : Decodable {
var array = [String]()

init(from decoder: Decoder) throws {
var arrayContainer = try decoder.unkeyedContainer()
while !arrayContainer.isAtEnd {
let string = try arrayContainer.decode(String.self)
let int = try arrayContainer.decode(Int.self)
array.append(String(int))
array.append(string)
}
}
}

let data = Data(jsonArray.utf8)
do {
let result = try JSONDecoder().decode(Item.self, from: data)
print(result.array)
} catch { print(error) }

Swift JSONDecode decoding arrays fails if single element decoding fails

One option is to use a wrapper type that attempts to decode a given value; storing nil if unsuccessful:

struct FailableDecodable<Base : Decodable> : Decodable {

let base: Base?

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.base = try? container.decode(Base.self)
}
}

We can then decode an array of these, with your GroceryProduct filling in the Base placeholder:

import Foundation

let json = """
[
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
},
{
"name": "Orange"
}
]
""".data(using: .utf8)!


struct GroceryProduct : Codable {
var name: String
var points: Int
var description: String?
}

let products = try JSONDecoder()
.decode([FailableDecodable<GroceryProduct>].self, from: json)
.compactMap { $0.base } // .flatMap in Swift 4.0

print(products)

// [
// GroceryProduct(
// name: "Banana", points: 200,
// description: Optional("A banana grown in Ecuador.")
// )
// ]

We're then using .compactMap { $0.base } to filter out nil elements (those that threw an error on decoding).

This will create an intermediate array of [FailableDecodable<GroceryProduct>], which shouldn't be an issue; however if you wish to avoid it, you could always create another wrapper type that decodes and unwraps each element from an unkeyed container:

struct FailableCodableArray<Element : Codable> : Codable {

var elements: [Element]

init(from decoder: Decoder) throws {

var container = try decoder.unkeyedContainer()

var elements = [Element]()
if let count = container.count {
elements.reserveCapacity(count)
}

while !container.isAtEnd {
if let element = try container
.decode(FailableDecodable<Element>.self).base {

elements.append(element)
}
}

self.elements = elements
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(elements)
}
}

You would then decode as:

let products = try JSONDecoder()
.decode(FailableCodableArray<GroceryProduct>.self, from: json)
.elements

print(products)

// [
// GroceryProduct(
// name: "Banana", points: 200,
// description: Optional("A banana grown in Ecuador.")
// )
// ]


Related Topics



Leave a reply



Submit