I have been stuck for a few days trying to decode a multidimensional JSON array from a URLSession call. This is my first project decoding JSON in SwiftUI. My attempts from reading up on methods others suggest do not seem to work.
Here is my JSON response from the server
"success": true,
"message": "Authorized",
"treeData": {
"Right": {
"G1P1": {
"Name": "John Johnson",
"ID": 387,
"SubText": "USA 2002"
},
"G2P1": {
"Name": "Tammy Johnson",
"ID": 388,
"SubText": "USA 2002"
},
"G2P2": {
"Name": "Susan Johnson",
"ID": 389,
"SubText": "USA 1955"
}
},
"Left": {
"G1P1": {
"Name": "Jane Doe",
"ID": 397,
"SubText": "USA 2002"
},
"G2P1": {
"Name": "John Doe",
"ID": 31463,
"SubText": "USA 2002"
},
"G2P2": {
"Name": "Susan Doe",
"ID": 29106,
"SubText": "USA 1958"
}
}
}
}
Here is my decode block of code
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
completion(.failure(.noData))
return
}
guard let treeResponse = try? JSONDecoder().decode([String: TreeResponse].self, from: data) else {
completion(.failure(.decodingError))
return
}
dump(treeResponse)
completion(.success("Hooray"))
}.resume()
And then here are my structs, which is the part I can't seem to figure out
struct TreeResponse: Codable {
let success: Bool
let message: String
let treeData: [String:SideData]
}
struct SideData: Codable {
let personKey: [String:PersonInfo]
}
struct PersonInfo: Codable {
let Name: String
let ID: Int
let SubText: String
}
My hope is to be able to access the decoded data as treeResponse.Right.G1P1.Name
Could really use help moving past this
CodePudding user response:
I will post this as an answer even if it cannot be one, but that way I can at least format it properly :-).
First of all you should learn to pose your questions in a manner that makes it as easy as possible for anyone to execute your code. Swift has a particularly helpful way of doing this, you can run a Playground on it. Then you should start whittling down your question to its essence which appears to be the JSON decode. JSONDecoder usually is very helpful in providing you with decent error messages on what it does not like about your JSON, but you have to print them. A suitable Playground would look as follows:
import UIKit
let jsonStr = """
{
"success": true,
"message": "Authorized",
"treeData": {
"Right": {
"G1P1": {
"Name": "John Johnson",
"ID": 387,
"SubText": "USA 2002"
},
"G2P1": {
"Name": "Tammy Johnson",
"ID": 388,
"SubText": "USA 2002"
},
"G2P2": {
"Name": "Susan Johnson",
"ID": 389,
"SubText": "USA 1955"
}
},
"Left": {
"G1P1": {
"Name": "Jane Doe",
"ID": 397,
"SubText": "USA 2002"
},
"G2P1": {
"Name": "John Doe",
"ID": 31463,
"SubText": "USA 2002"
},
"G2P2": {
"Name": "Susan Doe",
"ID": 29106,
"SubText": "USA 1958"
}
}
}
}
"""
struct TreeResponse: Codable {
let success: Bool
let message: String
let treeData: [String:SideData]
}
struct SideData: Codable {
let personKey: [String:PersonInfo]
}
struct PersonInfo: Codable {
let Name: String
let ID: Int
let SubText: String
}
let jsonData = jsonStr.data(using:.utf8)!
do {
let tree = try JSONDecoder().decode(TreeResponse.self, from: jsonData)
print(tree)
} catch {
print(tree)
}
This will yield a somewhat descriptive error message:
keyNotFound(CodingKeys(stringValue: "personKey", intValue: nil),
Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "treeData", intValue: nil),
_JSONKey(stringValue: "Right", intValue: nil)],
debugDescription: "No value associated with key
CodingKeys(stringValue: \"personKey\", intValue: nil)
(\"personKey\").", underlyingError: nil))
(Indentation mine and not particularly well thought out)
This starts pointing out your problems (of which you still seem to have a lot).
The first level of decode is somewhat ok, but the second level is woefully inadequate in its current form. There is no such thing as a personKey
in your JSON which would be required to fit it into a simple struct
. However you still might be able to coax it through some decode
method.
Considering you JSON that appears to be a bad choice and you should opt for properly modelling your tree with the given Left
and Right
keys, although this is probably scratching the limit of what Decodable
will do for you for free, so you will have to put in some more work to get this to work on a more involved example. If the keys on the following levels have any special significance you will have to put in a special decode
there too.
In any way, you should definitely learn to formulate your questions better.
CodePudding user response:
when our structure are not perfect to JSON so that's why get this types error and i've use JSONDecoder to retrieve the data from JSON couldn't read the data it's missing, though, such error yet get so needs to create quite perfect JSON models or create model with CodingKeys such like:
struct JSONData: Codable {
let success: Bool
let message: String
let treeData: TreeData
}
struct TreeData: Codable {
let treeDataRight, treeDataLeft: [String: Left]
enum CodingKeys: String, CodingKey {
case treeDataRight = "Right"
case treeDataLeft = "Left"
}
}
struct Left: Codable {
let name: String
let id: Int
let subText: String
enum CodingKeys: String, CodingKey {
case name = "Name"
case id = "ID"
case subText = "SubText"
}
}
For get JSON data to need JSONDecoder():
let jsonData = jsonStr.data(using:.utf8)!
do {
let tree = try JSONDecoder().decode(JSONData.self, from: jsonData)
dump(tree)
} catch {
print(error.localizedDescription)
}
Together with json, JSON model, JSONDecoder():
let jsonStr = """
{
"success": true,
"message": "Authorized",
"treeData": {
"Right": {
"G1P1": {
"Name": "John Johnson",
"ID": 387,
"SubText": "USA 2002"
},
"G2P1": {
"Name": "Tammy Johnson",
"ID": 388,
"SubText": "USA 2002"
},
"G2P2": {
"Name": "Susan Johnson",
"ID": 389,
"SubText": "USA 1955"
}
},
"Left": {
"G1P1": {
"Name": "Jane Doe",
"ID": 397,
"SubText": "USA 2002"
},
"G2P1": {
"Name": "John Doe",
"ID": 31463,
"SubText": "USA 2002"
},
"G2P2": {
"Name": "Susan Doe",
"ID": 29106,
"SubText": "USA 1958"
}
}
}
}
"""
struct JSONData: Codable {
let success: Bool
let message: String
let treeData: TreeData
}
struct TreeData: Codable {
let treeDataRight, treeDataLeft: [String: Left]
enum CodingKeys: String, CodingKey {
case treeDataRight = "Right"
case treeDataLeft = "Left"
}
}
struct Left: Codable {
let name: String
let id: Int
let subText: String
enum CodingKeys: String, CodingKey {
case name = "Name"
case id = "ID"
case subText = "SubText"
}
}
let jsonData = jsonStr.data(using:.utf8)!
do {
let tree = try JSONDecoder().decode(JSONData.self, from: jsonData)
dump(tree)
} catch {
print(error.localizedDescription)
}
Result: Result here
and i hope this would work and helpfully so try once