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
Complicated way with
Codable
- Decode the array with
unkeyedContainer
- Use a
while
loop with condition!isAtEnd
and decodeInt
in ado - catch
block. If it fails decodeString
- Decode the array with
Easy way with traditional
JSONSerialization
- Deserialize the object to
[CustomStringConvertible]
and map the array to[String]
with"\($0)"
- Deserialize the object to
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
Understanding Shorthand Closure Syntax for Map Function in Swift
Handle Swiftui and Corelocation with Mvvm-Pattern
Drawing a 3D Arc and Helix in Scenekit
Deploy App with Pre-Populated Core Data
Creating a Custom Scngeometry Polygon Plane with Scngeometryprimitivetype Polygon Crash/Error
Adding a Search Bar to Navigationview in Swiftui
Use Uipangesturerecognizer to Drag Uiview Inside Limited Area
Swift Didset Get Index of Array
Open File Dialog Crashes in Swift
Swiftui - Foreach Deletion Transition Always Applied to Last Item Only
"Cannot Inherit from Non-Open Class" Swift
How to Set an Attributed Title Color for State in Swift
Nsnumberformatter:Show 'K' Instead of ',000' in Large Numbers
How to Enumerate a Slice Using the Original Indices
Swift Why Strcmp of Backspace Returns -92
How to Use Dispatch Groups to Wait to Call Multiple Functions That Depend on Different Data