I have a json that one part of it is not constant and I don't know how to do the Codable struct.
Please see the example code under:
The fruits part is not constant so I'm not sure how to do the Codable struct. I tried to find answer on SOF but I can't find any for "non constant json Codable struct". I might not searching the correct key words.
Thank you.
Here is the example of the json:
{
"success": true,
"username": "app",
"data": {
"locations": {
"asia": {
"japan": {
"store_count": 5
},
"korea": {
"store_count": 3
}
}
},
"market": {
"fruits": {
"banana": {
"price": 50.00,
"count": 2
},
"apple": {
"price": 444.00,
"count": 16
},
"mango": {
"price": 28.00,
"count": 1
},
"peach": {
"price": 50.00,
"count": 2
},
"watermelon": {
"price": 50.00,
"count": 2
},
"blackberry": {
"price": 57.00,
"count": 2
}
}
}
}
Struct for the json
struct Markets: Codable {
let success: Bool?
let data: Data?
struct Data: Codable {
let locations: Locations
let market: Market
struct Locations: Codable {
let asia: Asia
struct Asia: Codable {
let japan: Stores
ler korea:
}
struct Stores: Codable {
let store_count: Int
}
}
struct Market: Codable {
var fruits: Type
struct Type: Codable {
// the fruits type are not constant.
}
}
}
}
}
Define the struct that is consistent:
struct FruitInfo: Codable {
var price: Double
var count: Int
}
And map those to String:
var fruits: [String: FruitInfo]
There are many other solutions if you want another type of structure (for example embedding the name of the fruit into the struct), but this is the simplest, and requires no custom init.
import Foundation
// MARK: - Test
struct Test: Codable {
let success: Bool
let username: String
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let locations: Locations
let market: Market
}
// MARK: - Locations
struct Locations: Codable {
let asia: Asia
}
// MARK: - Asia
struct Asia: Codable {
let japan, korea: Japan
}
// MARK: - Japan
struct Japan: Codable {
let storeCount: Int
enum CodingKeys: String, CodingKey {
case storeCount = "store_count"
}
}
// MARK: - Market
struct Market: Codable {
let fruits: Fruits
}
// MARK: - Fruits
struct Fruits: Codable {
let banana, apple, mango, peach: Apple
let watermelon, blackberry: Apple
}
// MARK: - Apple
struct Apple: Codable {
let price, count: Int
}
from https://app.quicktype.io/
simply enter your json and it gives you either class or struct
Related
This is the struct I am using and need your help in finding what will be the value of
data field.
struct ResponceData: Codable {
let dataRes: [DataResStruct]
struct DataResStruct: Codable {
let data: <what should be used>
let key: String
}
}
My responce data could be is like below:
Resp 1
{
"dataRes": [{
"data": [{
"key1_d": "val1_v",
"key2_d": "val2_v",
"key3_d": "val3_v"
}],
"key": "1"
}]
}
Resp 2
{
"dataRes": [{
"data": [{
"key1_A": "val1_B",
"key2_A": "val2_B",
"key3_A": "val3_B",
"key4_A": "val4_B",
"key5_A": "val5_B"
},
{
"key1_C": "val1_D",
"key1_C": "val1_D"
}
],
"key": "2"
}]
}
As described, this type is:
struct ResponceData: Codable {
let dataRes: [DataResStruct]
struct DataResStruct: Codable {
let data: [[String:String]]
let key: String
}
}
Whether that's really the correct type, or if it is particularly useful, depends on how close the actual data you're parsing is to what you've posted here.
I read a JSON-String from my api with a Date-Array.
[
{
"id": 1,
"headline": "How to messure the T8",
"descriptiontext": "Learn how to messure the T8",
"adddate": {
"date": "2022-03-02 00:00:00.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"changedate": {
"date": "2022-03-02 00:00:00.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
]
I had add a datemodel to the the class. without the date my decoding works and the objects would be viewed. But it seems that i have a fail in my decoding of the date.
Here is my struct
// MARK: - TutorialModel
struct TutorialModel: Identifiable, Codable {
let id: Int
let adddate, changedate: CustomDateModel
let headline, descriptiontext: String
}
// MARK: - CustomDateModel
struct CustomDateModel: Codable {
let date: String
let timezoneType: Int
let timezone: String
enum CodingKeys: String, CodingKey {
case date
case timezoneType
case timezone
}
}
And here is the part, were i try to Decode the JSON
guard let newTutorials = try? JSONDecoder().decode([TutorialModel].self, from: data) else { return }
DispatchQueue.main.async { [weak self] in
self?.tutorials = newTutorials
}
Could it be, that I have to read the data in a other way?
This question already has answers here:
Can we reuse struct on Swift? Or is there any other way?
(4 answers)
Closed 1 year ago.
Assuming I have two API calls
// /someurl/api/product-list
{
"status":0,
"message": "ok",
"data":{
"items":[
{
"id":1,
"name":"iPhone",
"desc":{
"en-us":"This is an iPhone",
"fr":"Ceci est un iPhone"
}
}
]
}
}
// /someurl/api/movie-list
{
"status":0,
"message": "ok",
"data":{
"items":[
{
"imdb_id":"tt0081505",
"title":"The Shining",
"short_desc":{
"en-us":"This is The Shining",
"fr":"C'est le Shining"
}
}
]
}
}
Now, both two api responses include the same structure of status, message(potentially would have pagination info object), except the data are different.
And In productModel
struct ProcuctAPIResponse: Codable {
let status: Int
let data: ProcuctAPIResponseData
let message: String?
}
struct ProcuctAPIResponseData: Codable {
let items: [Product]
}
struct Product: Codable {
let id: Int
let name: String?
let desc: [String: String]?
}
And In movieModel
struct MovieAPIResponse: Codable {
let status: Int
let data: MovieAPIResponseData
let message: String?
}
struct MovieAPIResponseData: Codable {
let items: [Movie]
}
struct Movie: Codable {
let imdb_id: Int
let title: String?
let short_desc: [String: String]?
}
My question is that is there a way I can create a BaseAPIResponse
something like
struct BaseAPIResponse: Codable {
let status: Int
let message: String?
}
then ProcuctAPIResponse and MovieAPIResponse can be extended from of it?
If not, how do you optimize these codes?
use protocols
protocol BaseAPIResponse: Codable {
let status: Int
let message: String?
}
Now implement the structs:
struct ProcuctAPIResponse: BaseAPIResponse {...}
movieModel
struct movieModel: BaseAPIResponse {...}
I'm trying to access a nested array in my SwiftUI code.
I have an array that looks like this:
{
"Count": 123,
"someKey": [
{
"Id": 12345,
"Images": [
28008,
28009
]
}
]
}
I'm trying to access the Images. So I came up with this code:
#ObservedObject var fetcherP = FetcherP()
public class FetcherP: ObservableObject {
#Published var sightingsP = [someKeyP]()
init(){
load()
}
func load() {
URLSession.shared.dataTask(with: request) { [self](data,response,error) in
do {
if let d = data {
let res = try JSONDecoder().decode(RootP.self, from: d)
DispatchQueue.main.async {
self.sightingsP = res.sightingsP
}
}else {
print("No Data")
}
} catch {
print (error)
}
}.resume()
}
}
and my codable:
struct RootP: Codable {
let count: Int
let someKeyP: [someKeyP]
enum CodingKeys: String, CodingKey {
case count = "count"
case someKeyP = "someKey"
}
}
struct someKeyP: Codable, Identifiable {
public var id: Int
public var images: [Int]
enum CodingKeys: String, CodingKey {
case id = "id"
case images = "images"
}
}
And this is how I try to use my code:
ForEach(fetcherP.sightingsP) {myimages in
**//How can I access the images here? I can't do foreach(myimages.images) {img in{
Text(img)
}**
}
Do I have to use double foreach? or is there any other way to do this?
any pointers would be appreciated.
There are a few things going wrong:
Your CodingKeys don't reflect the JSON -- case sensitivity matters
In ForEach, unless your model conforms to Hashable, you need to provide an id for it to use.
You can't provide an Int to Text -- you have to provide a String (I used string interpolation using "\(intValue)")
let jsonData = """
{
"Count": 123,
"someKey": [
{
"Id": 12345,
"Images": [
28008,
28009
]
}
]
}
""".data(using: .utf8)!
public class FetcherP: ObservableObject {
#Published var sightingsP = [someKeyP]()
init(){
load()
}
func load() {
do {
sightingsP = (try JSONDecoder().decode(RootP.self, from: jsonData)).someKeyP
} catch {
print(error)
}
}
}
struct RootP: Codable {
let count: Int
let someKeyP: [someKeyP]
enum CodingKeys: String, CodingKey {
case count = "Count"
case someKeyP = "someKey"
}
}
struct someKeyP: Codable, Identifiable {
public var id: Int
public var images: [Int]
enum CodingKeys: String, CodingKey {
case id = "Id"
case images = "Images"
}
}
struct ContentView : View {
#ObservedObject var fetcherP = FetcherP()
var body: some View {
VStack {
ForEach(fetcherP.sightingsP, id: \.id) { myImages in
ForEach(myImages.images, id: \.self) { imageNumber in
Text("\(imageNumber)")
}
}
}
}
}
Note that what I've included here is a complete, reproducible example -- it can be copied and pasted into Xcode and tested.
A few months back I started learning how to use the new codable and I posted this question Swift 4 decoding json using Codable. Right now I'm trying to use Realm and I've changed the model to follow the documents but I keep getting the error that it doesn't conform to decodable. I'm pretty lost as to how to make this work.
{
"success": true,
"message": "got the locations!",
"data": {
"LocationList": [
{
"LocID": 1,
"LocName": "Downtown"
},
{
"LocID": 2,
"LocName": "Uptown"
},
{
"LocID": 3,
"LocName": "Midtown"
}
]
}
}
class Location: Object, Decodable {
#objc dynamic var success: Bool = true
let data = List<LocationData>()
}
class LocationData: Object, Decodable {
let LocationList = List<LocationItem>()
}
class LocationItem: Object, Decodable {
#objc dynamic var LocID: Int!
#objc dynamic var LocName: String!
}
Instead of declaring your lists like this:
let data = List<LocationData>()
let LocationList = List<LocationItem>()
Declare them like this instead:
var data = LocationData()
var LocationList = [LocationItem]()
This confirms to the codable/decodable
Update:
I made a test which worked out with your models, tryout this:
struct Location: Decodable {
let success: Bool
let message: String
var data = LocationData()
}
struct LocationData: Decodable {
var LocationList = [LocationItem]()
}
struct LocationItem: Decodable {
var LocID: Int
var LocName: String
}
let json = "{ \"success\": true, \"message\": \"got the locations!\", \"data\": { \"LocationList\": [ { \"LocID\": 1, \"LocName\": \"Downtown\" }, { \"LocID\": 2, \"LocName\": \"Uptown\" }, { \"LocID\": 3, \"LocName\": \"Midtown\" } ] } }"
do {
let data = Data(json.utf8)
let decoded = try JSONDecoder().decode(Location.self, from: data)
print(decoded)
} catch let error {
print(error)
}
This will output:
Location(success: true, message: "got the locations!", data: CodableDecodable.LocationData(LocationList: [CodableDecodable.LocationItem(LocID: 1, LocName: "Downtown"), CodableDecodable.LocationItem(LocID: 2, LocName: "Uptown"), CodableDecodable.LocationItem(LocID: 3, LocName: "Midtown")]))
In your case your models should be:
class Location: Object, Decodable {
#objc dynamic var success: Bool = true
var data = LocationData()
}
class LocationData: Object, Decodable {
var LocationList = [LocationItem]()
}
class LocationItem: Object, Decodable {
#objc dynamic var LocID: Int!
#objc dynamic var LocName: String!
}