Create collection of JSONObjects from list of Kotlin objects - json

I have a room database from which I can get a List<LearningEvent>, which I then have to convert into a Collection<JSONObject> and return it. How can I efficiently do that?
Here's the LearningEvent class:
#Entity(tableName = "learningEvents")
data class LearningEvent(
#ColumnInfo(name = "learningeventid")
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
var sessionId: Long,
var time: Float,
var eventType: String,
var description: String,
var feedback: String
)
Here's the DAO:
#Query("SELECT * FROM learningEvents WHERE sessionId = :sessionId")
suspend fun getAllLearningEvents(sessionId: Long?): List<LearningEvent>
And here's my non-working/non-building code for the getEvents() function:
override suspend fun getEvents(): Collection<JSONObject> =
withContext(Dispatchers.IO) {
Log.d("\ngetEvents(eventType)", currentSessionId.toString())
if (currentSessionId === null) {
throw Exception("Current session Id is null; Session cannot be retrieved.")
}
var gson = Gson();
// Return JSON object collection of table rows.
var learningEvents = db.learningEventDao().getAllLearningEvents(currentSessionId);
var returnCollection = emptyList<JSONObject>();
learningEvents.forEach{element ->
var singleObject = gson.toJsonTree(element);
returnCollection += singleObject;
}
return#withContext returnCollection;
}

In my humble opinion, you can convert LearningEvent to JSONObject using .toJson. Here the sample code:
var learningEvents = db.learningEventDao().getAllLearningEvents(currentSessionId);
var returnCollection = emptyList<JSONObject>();
learningEvents.forEach{ element ->
val singleObject = gson.toJson(element);
returnCollection += JSONObject(singleObject);
}

Related

How to return object from nested json

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.

Updating json model and retaining/converting existing data - Swift

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.

Iterating through JSON data

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()))
}

Handling 2 keys for same value in single class

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)}
}

Pass a JsonObject to Model SWIFT

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])