Issue parsing JSON not in array format - json

Essentially I would like to take the following JSON and use the data within my project.
To parse the data I need to create a structure - in the past I used this for arrays:
Structure:
struct ArrayStruct: Codable {
let info1: String
let info2: String
}
Array:
[
{
"info1": "Information",
"info2": "More"
}
]
What would the structure for the following data look like?
{
"error": false,
"response": "",
"message": "",
"general": {
"customer": "Customer1",
"code": "CO1234",
"name": "Machine Name",
"category": "machine"
},
"details": {
"taken": "July 15th 2020",
"created": "November 2nd 2020",
"packed": "",
"delivered": ""
}
}

I made a structure using struct with codeable for the data you gave
struct MainResponse : Codable {
let details : Detail?
let error : Bool?
let general : General?
let message : String?
let response : String?
// coding keys not required for this as the data keys are same but added just as an example
enum CodingKeys: String, CodingKey {
case details = "details"
case error = "error"
case general = "general"
case message = "message"
case response = "response"
}
}
struct Detail : Codable {
let created : String?
let delivered : String?
let packed : String?
let taken : String?
}
struct General : Codable {
let category : String?
let code : String?
let customer : String?
let name : String?
}

First You need to create your data Struct like this
struct MainResponse : Codable {
let error : Bool?
let response : String?
let message : String?
let general : General?
let details : Details?
}
Make your General Struct like this
struct General : Codable {
let customer : String?
let code : String?
let name : String?
let category : String?
}
Make your Detail Struct like this
struct Details : Codable {
let taken : String?
let created : String?
let packed : String?
let delivered : String?
}

its easy to create structures now days, there are various tools available, just input the response you will get the data structure, used the following to generate the structure
quicktype.io
// MARK: - Customer
struct Customer: Codable {
let error: Bool?
let response, message: String?
let general: General?
let details: Details?
}
// MARK: - Details
struct Details: Codable {
let taken, created, packed, delivered: String?
}
// MARK: - General
struct General: Codable {
let customer, code, name, category: String?
}

Related

Parse nested data from JSON using Codable

I am trying to parse nested data from JSON response but not getting success. Below is what i tried so far and the json response trying to parse.
// JSON
{
"statusCode": 200,
"success": true,
"data": {
"tDetail": [
{
"roleId": null,
"id": 34,
"userId": 126,
"catId": null,
"importId": null,
"name": "My task from postman",
"myday": 1,
"important": 0,
"completed": 0,
"dateCreated": "2020-02-10T09:05:04.000Z",
"dateModified": "2020-02-10T09:05:04.000Z"
}
],
"steps": [],
"files": [],
}
}
// Struct
struct MyDayAndTaskDetails: Codable
{
let data : [MyTaskDetail]
}
struct MyTaskDetail : Codable {
let roleId, taskId, userId, catId, important, completed, recurring, myday : Int?
let repeatType, name, duedate, reminder, frequency, weekdays, notes, baseurl : String?
let steps : [Steps]
let files : [Files]
private enum CodingKeys: String, CodingKey {
case taskId = "id"
case userId = "userId"
case roleId = "roleId"
case catId = "catId"
case myday = "myday"
case name = "name"
case notes = "notes"
case duedate = "duedate"
case reminder = "reminder"
case recurring = "recurring"
case repeatType = "repeatType"
case important = "important"
case completed = "completed"
case frequency = "frequency"
case weekdays = "weekdays"
case baseurl = "baseurl"
case steps = "Steps"
case files = "Files"
}
}
struct Steps : Codable {
let stepName : String?
let status, stepId : Int?
private enum CodingKeys: String, CodingKey {
case stepName = "stepName"
case status = "status"
case stepId = "stepId"
}
}
struct Files : Codable {
let fileName : String?
private enum CodingKeys: String, CodingKey {
case fileName = "fileName"
}
}
You missed one level
struct MyDayAndTaskDetails: Codable {
let data : Detail
}
struct Detail: Codable {
let tDetail: [MyTaskDetail]
let steps : [Steps]
let files : [Files]
}
and so on
Use this structs, below structs are sufficient for given json. If you have more keys in your json then you can add them into their respective structs.
struct MyDayAndTaskDetails : Codable {
let data : Task?
let statusCode : Int?
let success : Bool?
}
struct Task : Codable {
let files : [Files]?
let steps : [Steps]?
let tDetail : [TDetail]?
}
struct TDetail : Codable {
let catId : String?
let completed : Int?
let dateCreated : String?
let dateModified : String?
let id : Int?
let importId : String?
let important : Int?
let myday : Int?
let name : String?
let roleId : String?
let userId : Int?
}
struct Steps : Codable {
let stepName : String?
let status: Int?
let stepId : Int?
}
struct Files : Codable {
let fileName : String?
}
And decode data with
let decoder = JSONDecoder()
let response = try decoder.decode(MyDayAndTaskDetails.self, from: data)

Attempt at utilizing decodable

I am trying to create decodable structs for the JSON that I am receiving, but I keep getting:
keyNotFound(CodingKeys(stringValue: "inquiry_quantity", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "orders", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys...
This is the JSON that I am running through my decoder:
{
"orders_important": 2,
"orders_inquiry": 2,
"orders": [
{
"information": {
"total_price": 12.0,
"order_stage": "CONFIRMED",
"total_cases": 333,
"scanned": false,
"inquiry": false,
"foodhub": "shdw.fh1#agg.com",
"identifier": "GPO00024",
"delivery_date": "2019-11-04"
},
"details": {
"pack_size": "40#",
"cases": 333,
"plu_code": 12434,
"crop_plan": 1633,
"commodity": "Conventional Bananas, , 40#"
}
},
{
"information": {
"inquiree": "FARMER",
"total_price": 23.5,
"order_stage": "INQUIRY",
"inquiry_quantity": 0,
"total_cases": 56,
"scanned": false,
"inquiry": true,
"foodhub": "shdw.fh1#agg.com",
"inquiry_type": "PRICE",
"identifier": "GPO00027",
"inquiry_price": 26.5,
"delivery_date": "2019-11-05",
"inquiry_details": "You have requested $26.5 per case, instead of $23.5."
},
"details": {
"pack_size": "40#",
"cases": 12,
"plu_code": 12434,
"crop_plan": 1589,
"commodity": "Conventional Bananas, , 40#"
}
},
],
"orders_count": 4
}
My decodable structs: (EDIT: Removed Init)
struct OrderBundle: Decodable{
let orders_important : Int
let orders_inquiry:Int
let orders_count: Int
let orders: [Order]
}
struct Order: Decodable {
let inquiry_quantity : Int
let inquiry_price : Int
let inquiry_info : String
let price : Int
let cropPlan : Int
let identifier: String
let deliveryDate : String
let quantity : Int
let unit : String
let status : String
let destination : String
let commodity: String
}
And lastly calling my decoder:
let orderData = try
JSONDecoder().decode(OrderBundle.self, from: data!)
print(orderData)
I have tried it with and without the Init in my Orders and both ways I am still getting the same error.
The main issue is that any dictionary must be decoded to an extra struct if init(from decoder is not provided. So basically the structure of the model must be
struct OrderBundle: Decodable {
struct Information : Decodable {
struct Details : Decodable {
The complete structs are as follows, many struct members must be declared as optional and inquiryPrice is Double, not Int. convertFromSnakeCase converts the snaked_cased keys to camelCased struct members.
struct OrderBundle : Decodable{
let ordersImportant : Int
let ordersInquiry : Int
let ordersCount : Int
let orders : [Order]
}
struct Order : Decodable {
let information : Information
let details : Details
struct Information : Decodable {
let inquiryQuantity : Int?
let inquiryPrice : Double?
let inquiryInfo : String?
let price : Int?
let cropPlan : Int?
let identifier : String
let deliveryDate : String
let quantity : Int?
let unit : String?
let status : String?
let destination : String?
let commodity: String?
}
struct Details : Decodable {
let packSize : String
let cases : Int
let pluCode : Int
let cropPlan : Int
let commodity : String
}
}
And the code to decode it
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let orderData = try decoder.decode(OrderBundle.self, from: data!)
print(orderData)

How to map this heterogeneous object with the model in swift?

Okay, so I am stuck at decoding the last item of this particular json by this the model, "payload" is always nil, Inside this "payload" object I can make my own json structure, I am able to decode the "text" but when it comes to the last item which is "payload", it is not working and is always nil.
I am not using any third-party library.
My Model Class.
struct DailougeFlowModel : Decodable {
// private enum CodingKeys : String, CodingKey {
// case responseId = "responseId"
// case queryResult = "queryResult"
// }
let responseId : String?
let queryResult : QueryResult?
}
struct QueryResult: Decodable {
// private enum CodingKeys : String, CodingKey {
// case fulfillmentText = "fulfillmentText"
// case fulfillmentMessages = "fulfillmentMessages"
// }
let fulfillmentText : String?
let fulfillmentMessages : [FulfillmentMessages]?
}
struct FulfillmentMessages: Decodable {
let text : TextModel?
let payLoad : Questions?
}
struct TextModel: Decodable {
let text : [String]?
}
struct Questions : Decodable{
let questions : [String]?
}
This json is what I am getting from the dailogeflow(V2). I am integrating a chatbot in the application.
{
"responseId": "2b879f78-cc05-4735-a7e8-067fdb53a81d-f6406966",
"queryResult": {
"fulfillmentMessages": [
{
"text": {
"text": [
"Text Here"
]
}
},
{
"text": {
"text": [
"Another Reply For Hi"
]
}
},
{
"payload": {
"questions": [
"Question One",
"Question Two",
"Question Three",
"Question Four"
]
}
}
]
}
}
Specify the inner model names as it is in the json response, if you want to specify your own model name then you would need to set an enum in each model just like the first model 'ResponseModel'
// MARK: - ResponseModel
struct ResponseModel: Codable {
let responseID: String
let queryResult: QueryResult
enum CodingKeys: String, CodingKey {
case responseID = "responseId"
case queryResult
}
}
// MARK: - QueryResult
struct QueryResult: Codable {
let fulfillmentMessages: [FulfillmentMessage]
}
// MARK: - FulfillmentMessage
struct FulfillmentMessage: Codable {
let text: Text?
let payload: Payload?
}
// MARK: - Payload
struct Payload: Codable {
let questions: [String]
}
// MARK: - Text
struct Text: Codable {
let text: [String]
}

Why are certain variables turning up nil?

I'm trying to decode this json but certain variables are nil. Most of these seem to be okay it's just a few that are not working properly. I don't have much experience with swift so I'm kind of at a loss of what to try next.
mycode:
struct Attr : Decodable {
let page: String?
let perpage: String?
let totalpages: String?
let total: String?
}
struct Images : Decodable {
let text: String?
let size: String?
}
struct Artist : Decodable {
let name: String?
let mbid: String?
let url: String?
}
struct Streamable : Decodable {
let text: String?
let fulltrack: String?
}
struct Track : Decodable {
let name: String?
let duration: String?
let playcount: String?
let listeners: String?
let mbid: String?
let url: String?
let streamable: Streamable?
let artist: Artist?
let images: [Images]?
}
struct Tracks : Decodable {
let track:[Track]?
}
struct Container : Decodable {
let tracks: Tracks?
let attr: Attr?
}
json:
{
"tracks": {
"track": [
{
"name": "bad guy",
"duration": "0",
"playcount": "870682",
"listeners": "125811",
"mbid": "",
"url": "https://www.last.fm/music/Billie+Eilish/_/bad+guy",
"streamable": {
"#text": "0",
"fulltrack": "0"
},
"artist": {
"name": "Billie Eilish",
"mbid": "",
"url": "https://www.last.fm/music/Billie+Eilish"
},
"image": [
{
"#text": "https://lastfm-img2.akamaized.net/i/u/34s/88d7c302d28832b53bc9592ccb55306b.png",
"size": "small"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/64s/88d7c302d28832b53bc9592ccb55306b.png",
"size": "medium"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/174s/88d7c302d28832b53bc9592ccb55306b.png",
"size": "large"
},
{
"#text": "https://lastfm-img2.akamaized.net/i/u/300x300/88d7c302d28832b53bc9592ccb55306b.png",
"size": "extralarge"
}
]
},
...
images should contain an array of Images instead of nil, the majority of the other variables seem to be okay though
This is because when dealing with decodable the keys used in your serialized data format have to match the property names; In your case, the Image type contains text and size properties but the json contains #text and size (text =/= #text); That's also applicable for the type name Images not image.
However, citing from Encoding and Decoding Custom Types:
If the keys used in your serialized data format don't match the
property names from your data type, provide alternative keys by
specifying String as the raw-value type for the CodingKeys
enumeration.
Add CodingKeys as:
struct Images : Decodable {
let text: String?
let size: String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case size
}
}
Tip: it is useful to use try/catch when facing an error while decoding your data:
do {
let result = try JSONDecoder().decode(Images.self, from: data)
} catch {
print(error.localizedDescription)
}
At this point it would print the error which might helpful to understand the issue most of the time.
Add CodingKey enum to map fields with # in the name
struct Images : Decodable {
let text: String?
let size: String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case size
}
}
struct Streamable : Decodable {
let text: String?
let fulltrack: String?
enum CodingKeys: String, CodingKey {
case text = "#text"
case fulltrack
}
}
You also have an error in your Trackstruct, change images to image (or use CodingKey mapping there to). For more on decoding json see Apple's doc

Swift 4 Decode json with the ' - ' letter

How does the swift 4 Decodable protocol work with the ' - ' letter?
For example:
[{
"trigger": {
"url-filter": "webkit.org",
"resource-type": ["image"]
},
"action": {
"selector": "#logo",
"type": "block"
}
}]
In My Swift Class:
struct blockerJson : Decodable {
let action : action
let trigger : trigger
struct action : Decodable {
let selector : String
let type : String
}
struct trigger : Decodable {
let urlFilter : String
let resourceType : String
}
}
I don't know how to change the class, but the json can't change...
This code parses trigger block
struct Trigger: Decodable {
var urlFilter: String
var resourceType: [String]
enum CodingKeys: String, CodingKey {
case urlFilter = "url-filter"
case resourceType = "resource-type"
}
}