I have nested JSON and I'm trying to return an object and update UI. However, I can access properties only from Result array, but can't reach Step from AnalyzedInstactions.
Here are classes that represent data in the JSON. Generated it with https://app.quicktype.io
import UIKit
class ResultArray: Codable {
var results = [Result]()
}
class Result: Codable, CustomStringConvertible {
var title: String = ""
var image = ""
var readyInMinutes: Int? = 0
var servings: Int? = 0
var cuisines = [String]()
var dishTypes = [String]()
var diets = [String]()
var occasions = [String]()
var analyzedInstructions = [AnalyzedInstruction]()
var description: String {
return "\nResults - Name: \(title), Summary: \(String(describing: readyInMinutes ?? nil)), \(String(describing: servings ?? nil)) "
}
}
// MARK: - Steps
class AnalyzedInstruction: Codable {
var name: String? = ""
var steps = [Step]()
}
class Step: Codable {
var number: Int = 0
var step: String = ""
}
Here is my parse method
private func parse(data: Data) -> [Result] {
do {
let decoder = JSONDecoder()
let result = try decoder.decode(ResultArray.self, from: data)
return result.results
} catch {
print("JSON Error: \(error)")
return []
}
}
try this simple code to access your steps from AnalyzedInstructions:
let results: [Result] = parse(data: theData)
if let firstResult = results.first {
if let firstAnaInst = firstResult.analyzedInstructions.first {
for step in firstAnaInst.steps {
print("--> step.step: \(step.step) step.number: \(step.number)")
}
} else { print(" NO analyzedInstructions") }
} else { print(" NO results") }
if you want all steps:
for result in results {
for anaInst in result.analyzedInstructions {
for step in anaInst.steps {
print("--> step.step: \(step.step) step.number: \(step.number)")
}
}
}
PS: if appropriate, you may consider using struct instead of class for your json models.
I have a data model which looks as such:
struct MacroLog : Codable {
var date: Date = Date()
var type: MacroType
var beforeValue: Int
var afterValue: Int
var description: String
var isOverwrite: Bool = false
func valueDifference() -> Int {
return afterValue - beforeValue
}
}
And I need to update it with adding a timestamp property as such:
struct MacroLog : Codable {
let timestamp: Date
var date: Date = Date()
var type: MacroType
var beforeValue: Int
var afterValue: Int
var description: String
var isOverwrite: Bool = false
func valueDifference() -> Int {
return afterValue - beforeValue
}
}
What would I need to do, to have my existing MacroLog entities retained or converted to the new model data? I know it stays retained so this is basically me asking how I properly manage making model changes of any kind in these situations.
I update new entries the following way:
private func updateLogs(logs: [MacroLog]) {
var logsData = Data()
do {
let encoder = JSONEncoder()
logsData = try encoder.encode(logs)
_ = (logsData as NSData).write(to: fileUrl as URL, atomically: true)
} catch {
print("failed - \(error)")
}
}
My question was solved by the answer in the comment about making the timestamp optional. That way, all my previous log entries are retained and loaded going forward.
I am generating a JSON output in php which i need to use to fill line chart in android application writen in kotlin.
The JSON data is:
[{"reading_temperature":"14","hour":"01"},{"reading_temperature":"14","hour":"02"},{"reading_temperature":"14","hour":"03"},{"reading_temperature":"14","hour":"04"},{"reading_temperature":"14","hour":"05"},{"reading_temperature":"14","hour":"06"},{"reading_temperature":"14","hour":"07"},{"reading_temperature":"14","hour":"08"},{"reading_temperature":"14","hour":"09"},{"reading_temperature":"14","hour":"10"},{"reading_temperature":"14","hour":"11"},{"reading_temperature":"14","hour":"12"},{"reading_temperature":"14","hour":"13"},{"reading_temperature":"14","hour":"14"},{"reading_temperature":"14","hour":"15"},{"reading_temperature":"14","hour":"16"},{"reading_temperature":"14","hour":"17"},{"reading_temperature":"14","hour":"18"},{"reading_temperature":"14","hour":"19"},{"reading_temperature":"14","hour":"20"},{"reading_temperature":"14","hour":"21"},{"reading_temperature":"14","hour":"22"},{"reading_temperature":"14","hour":"23"}]
This is the part of the code where i get the JSON:
override fun doInBackground(vararg params: String?): String? {
var response:String?
try{
response = URL("https://127.0.0.1/weatherStation/temperatureDaily.php").readText(
Charsets.UTF_8
)
}catch (e: Exception){
response = null
}
return response
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
try {
val jsonObj = JSONObject(result)
} catch (e: Exception) {
}
}
Now i need to use that data to populate the line chart, this is hardcoded data which i need to exchange with JSON
private fun setupLineChartDataTemperatura() {
val yVals = ArrayList<Entry>()
//This next part i need to dynamically generate from JSON,
//first float value and last string value should be `"hour":"xx"`
// and second float value should be `[{"reading_temperature":"xx"`
yVals.add(Entry(0f, 4f, "0"))
yVals.add(Entry(1f, 5f, "1"))
yVals.add(Entry(1.5f, 4f, "1.5"))
yVals.add(Entry(2f, 5f, "2"))
yVals.add(Entry(3f, 3f, "3"))
yVals.add(Entry(4f, 2f, "4"))
val set1: LineDataSet
set1 = LineDataSet(yVals, "Temperatura")
val dataSets = ArrayList<ILineDataSet>()
dataSets.add(set1)
val data = LineData(dataSets)
chart1.setData(data)
chart1.description.isEnabled = false
chart1.legend.isEnabled = true
chart1.setPinchZoom(false)
chart1.xAxis.enableGridDashedLine(5f, 5f, 0f)
chart1.axisRight.enableGridDashedLine(5f, 5f, 0f)
chart1.axisLeft.enableGridDashedLine(5f, 5f, 0f)
chart1.setDrawGridBackground(true)
chart1.xAxis.labelCount = 11
chart1.xAxis.position = XAxis.XAxisPosition.BOTTOM
chart1.setTouchEnabled(true)
chart1.invalidate()
}
I have managed to do it:
val jsonTemperatureData = JSONArray(result)
for (i in 0 until jsonTemperatureData.length()) {
val item = jsonTemperatureData.getJSONObject(i)
val reading_temperature = item.getString("reading_temperature")
val hour = item.getString("hour")
yVals.add(Entry(hour.toFloat(), reading_temperature.toFloat(), hour.toString()))
}
I have a use case in which I get score form JSON using
let score = json["score_test"].arrayValue.map {Score.decode(json: $0)}
I have to reuse this class for a response in which only key for the value changes i.e
let score = json["score"].arrayValue.map {Score.decode(json: $0)}
Is there a way to achieve this so that I get the data of Score object whether the key is score_test or score depending on the JSON using the same class?
Also I tried using nil check but since the object is initialized that is not working.
Model of Score:
class Score: Object, Decoder {
dynamic var id: String = ""
dynamic var title: String = ""
dynamic var body: String = ""
dynamic var cardOrder: Int = 0
dynamic var video: Video? = nil
override static func primaryKey() -> String? {
return "id"
}
typealias T = Score
// MARK: Decoder method
static func decode(json: JSON) -> Score {
let id = json["_id"].stringValue
let title = json["title"].stringValue
let body = json["data"].stringValue
let cardOrder = json["card_order"].intValue
var video: Video?
if (json["video"].exists()) {
video = Video.decode(json: json["video"])
}
let score = Score()
score.id = id
score.title = title
score.body = body
score.video = video
score.cardOrder = cardOrder
return score
}
}
From what I understand, score is of type [Score] so what I'd do is:
var score = json["score_test"].arrayValue.map {Score.decode(json: $0)}
if score.isEmpty {
score = json["score"].arrayValue.map {Score.decode(json: $0)}
}
I am trying to pass a JSON object from the list of returned results to a Model class of User. I want to do all of the assignments / parsing inside of the user object.
I keep getting the message - cannot invoke User with argument of type JSON
Any hints?
let post = JSON(data)
println("The post is: " + post.description)
var user : User
user(post[0])
println(user.getName())
import SwiftyJSON
class User {
var ObjectId = ""
var FirstName = ""
var LastName = ""
var Organization = ""
var CallSign = ""
init(sObjectId : String, sFirstName : String, sLastName : String, sOrganization : String, sCallSign : String)
{
ObjectId = sObjectId
FirstName = sFirstName
LastName = sLastName
Organization = sOrganization
CallSign = sCallSign
}
init(sUser : JSON) {
self.ObjectId = sUser["_id"].string!
self.FirstName = sUser["firstName"].string!
self.LastName = sUser["lastName"].string!
self.Organization = sUser["organization"].string!
}
you have to call the appropriate initializer directly
let post = JSON(data)
println("The post is: " + post.description)
var user = User(sUser: post[0])