Kotlin gson.fromJSON doesn't allow initialization at declaration - json

Using Kotlin:
I have a class with different variable names than my JSON field names. When I call gson.fromJSON(string, Array<MyClass>::class.java, the initialization at declaration doesn't occur.
My understand from another post that declaration isn't called with constructor that only the only the constructor is called.
How, then do I easily get my JSON data into my class without calling the gson.fromJSON and the calling my class?
I'm not a very advanced developer. I'm looking for code as simple as possible that is efficient systematically.
What I want to do is:
val gson = Gson()
val seriesFirebaseList = gson.fromJson(seriesJSONArrayString, Array<Series>::class.java).toList()
class Series(Series_Unique_ID: String,
Series_Record_Name: String,
Series_Name: String,
Series_From_Year: Int,
Series_To_Year: Int?,
Is_Active: Int,
Logo_Image_Name: String,
Sort_Order: Int) {
val uniqueId: String = Series_Unique_ID
val recordName: String = Series_Record_Name
val seriesName: String = Series_Name
val fromYear: Int = Series_From_Year
var toYear: Int? = Series_To_Year
var isActive: Boolean = Is_Active == 1
val logoImageName: String = Logo_Image_Name
val sortOrder: Int = Sort_Order
// generated data
var imageFirebaseURLString: String? = null
var isFavoriteSeries = false
}
But, all the variables end up null or default value, not the JSON values.
The only thing I can get to work which seems really inefficient is to call the JSON firebase class then convert that to my class
val gson = Gson()
// create seriesFirebase
val seriesFirebaseList = gson.fromJson(seriesJSONArrayString, Array<SeriesFirebase>::class.java).toList()
val seriesList = mutableListOf<Series>()
seriesFirebaseList.forEach { seriesFirebase ->
val series = Series(seriesFirebase)
seriesList.add(series)
}
// series models
class Series(seriesFirebase: SeriesFirebase) {
val uniqueId: String = seriesFirebase.Series_Unique_ID
val recordName: String = seriesFirebase.Series_Record_Name
val seriesName: String = seriesFirebase.Series_Name
val fromYear: Int = seriesFirebase.Series_From_Year
var toYear: Int? = seriesFirebase.Series_To_Year
var isActive: Boolean = seriesFirebase.Is_Active == 1
val logoImageName: String = seriesFirebase.Logo_Image_Name
val sortOrder: Int = seriesFirebase.Sort_Order
// generated data
var imageFirebaseURLString: String? = null
var isFavoriteSeries = false
}
// used to convert Gson/JSON
class SeriesFirebase(val Series_Unique_ID: String,
val Series_Record_Name: String,
val Series_Name: String,
val Series_From_Year: Int,
val Series_To_Year: Int?,
val Is_Active: Int,
val Logo_Image_Name: String,
val Sort_Order: Int) {
constructor() : this(
"",
"",
"",
0,
2150,
0,
"",
0
)
override fun toString(): String {
return ("series: uniqueId: " + Series_Unique_ID
+ "; recordName: " + Series_Name
+ "; name: " + Series_Name
+ "; fromYear: " + Series_From_Year
+ "; toYear: " + Series_To_Year
+ "; isActive: " + Is_Active
+ "; logoImageName: " + Logo_Image_Name
+ "; sortOrder: " + Sort_Order)
}
}

You can mark fields of Series class with #SerializedName. E.g.
#SerializedName("Series_Record_Name")
val recordName: String

Related

I tried to parse a JSON file on my app but even if there is no error, my app doesn't run on the emulator

The problem might be that the app can't reach the json file I tried to parse it to.No error pops up in the run or build but it stops at"11/23 23:14:38: Launching 'app' on Device 4.Install successfully finished in 325 ms."Below is my code:
MainActivity
class MainActivity : AppCompatActivity() {
#SuppressLint("SuspiciousIndentation", "NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val usersList: ArrayList<UserModelClass> = ArrayList()
try {
val obj = JSONObject(getJSONFromAssets()!!)
val usersArray = obj.getJSONArray("owner")
for (i in 0 until usersArray.length()) {
val user = usersArray.getJSONObject(i)
val login = user.getInt("id")
val name = user.getString("name")
val url = user.getString("url")
val followers_url = user.getString("followers")
val starred_url = user.getString("stars")
val userDetails =
UserModelClass(login, name, url, followers_url , starred_url)
// add the details in the list
usersList.add(userDetails)
}
} catch (e: JSONException) {
//exception
e.printStackTrace()
}
val recyclerView = findViewById<RecyclerView>(R.id.rvUsersList)
val adapter = UserAdapter(this, usersList)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapter
adapter.notifyDataSetChanged()
}
private fun getJSONFromAssets(): String? {
var json: String? = null
val charset: Charset = Charsets.UTF_8
try {
val myUsersJSONFile = assets.open("JSON.json")
val size = myUsersJSONFile.available()
val buffer = ByteArray(size)
myUsersJSONFile.read(buffer)
myUsersJSONFile.close()
json = String(buffer, charset)
} catch (ex: IOException) {
ex.printStackTrace()
return null
}
return json
}
}
UserAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
class UserAdapter( val context: Context, val items: ArrayList<UserModelClass>) :
RecyclerView.Adapter<UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
return UserViewHolder(
LayoutInflater.from(context).inflate(
R.layout.item_user,
parent,
false
)
)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int {
return items.size
}
}
UserViewHolder
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each item to
var tvId : TextView
var tvName : TextView
var tvURL : TextView
var tvFollowers : TextView
var tvStars : TextView
init {
tvId = view.findViewById(R.id.tvId)
tvName = view.findViewById(R.id.tvName)
tvURL = view.findViewById(R.id.tvURL)
tvFollowers = view.findViewById(R.id.tvFollowers)
tvStars = view.findViewById(R.id.tvStars)
}
fun bind(item: UserModelClass) {
tvId.text = item.login.toString()
tvName.text = item.node_id
tvURL.text = item.avatar_url
tvFollowers.text = item.followers_url.toString()
tvStars.text = item.starred_url.toString()
}
}
UserModelClass
class UserModelClass(
val login: Int,
val node_id: String,
val avatar_url: String,
val followers_url: String,
val starred_url: String
)
Json.json
I don't need all the information from the json, I selected some of them
{
"total_count": 357602,
"incomplete_results": false,
"items": [
{
"id": 23096959,
"node_id": "...",
"name": "...",
"full_name": "...",
"private": false,
"owner": {
"login": "...",
"id": 4314092,
"node_id": "...",
"avatar_url": "https://...",
"url": "https://api.github.com/users/golang",
"followers_url": "https://api.github.com/users/golang/followers",
"following_url": "https://api.github.com/users/golang/following{/other_user}",
"starred_url": "https://api.github.com/users/golang/starred{/owner}{/repo}",
},
Actually it seems you are parsing it wrong. In your json, items is the array and owner is an object inside items array item.
You cannot access owner directly. Also you should give exact keys.
Like "followers_url" not just "followers"
Try the below code.
try {
val obj = JSONObject(getJSONFromAssets()!!)
val itemsArray = obj.getJSONArray("items")
for (i in 0 until itemsArray.length()) {
val user = itemsArray.getJSONObject(i)
val name = user.getString("name") // Getting name from item object
val owner = user.getJSONObject("owner") //Getting owner object from item object
//And below you need to get items from owner object not the user object.
val login = owner.getInt("id")
val url = owner.getString("url")
val followers_url = owner.getString("followers_url")
val starred_url = owner.getString("starred_url")
val userDetails =
UserModelClass(login, name, url, followers_url , starred_url)
// add the details in the list
usersList.add(userDetails)
}
} catch (e: JSONException) {
//exception
e.printStackTrace()
}

Create collection of JSONObjects from list of Kotlin objects

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

Can't parse json in Kotlin

I have a json like this:
{"ok":true,"result":[{"update_id":853803195,
"message":{"message_id":313,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594723984,"text":"/start","entities":[{"offset":0,"length":6,"type":"bot_command"}]}},{"update_id":853803196,
"message":{"message_id":314,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594723986,"text":"e"}},{"update_id":853803197,
"message":{"message_id":325,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594734252,"text":"\ud83d\ude06"}},{"update_id":853803198,
"message":{"message_id":328,"from":{"id":104906563,"is_bot":false,"first_name":"AL","username":"alzvaracc","language_code":"en"},"chat":{"id":104906563,"first_name":"AL","username":"alzvaracc","type":"private"},"date":1594736358,"text":"5"}}]}
I'm using klaxon library.
Like in the first example I created a class:
class Response(val ok: Boolean, val result: String)
I was trying to save the second parameter to a string, so I could parse it, too, later. But I get a exception like this:
Unable to instantiate Response with parameters [ok: true, result: [, , , ]]
I tried making result a JsonObject (or a JsonArray of JsonObjects) and got this
Unable to instantiate JsonObject with parameters []
The only thing that more or less worked was a List<Any>. Result becomes a list of java objects like this:
[java.lang.Object#680362a, java.lang.Object#3569edd5, java.lang.Object#1f651cd8, java.lang.Object#7d0332e1]
But I don't know how to deal with them. So my question is what do I do? How do I get the result I will be able to work with?
Try this class
import com.beust.klaxon.*
private val klaxon = Klaxon()
data class User (
val ok: Boolean,
val result: List<Result>
) {
public fun toJson() = klaxon.toJsonString(this)
companion object {
public fun fromJson(json: String) = klaxon.parse<User>(json)
}
}
data class Result (
#Json(name = "update_id")
val updateID: Long,
val message: Message
)
data class Message (
#Json(name = "message_id")
val messageID: Long,
val from: From,
val chat: Chat,
val date: Long,
val text: String,
val entities: List<Entity>? = null
)
data class Chat (
val id: Long,
#Json(name = "first_name")
val firstName: String,
val username: String,
val type: String
)
data class Entity (
val offset: Long,
val length: Long,
val type: String
)
data class From (
val id: Long,
#Json(name = "is_bot")
val isBot: Boolean,
#Json(name = "first_name")
val firstName: String,
val username: String,
#Json(name = "language_code")
val languageCode: String
)

How to map a JSON string to Kotlin Map

I have a simple Kotlin program that access a Mongo database and produce a JSON string as below;
"{
"_id" : { "$oid" : "593440eb7fa580d99d1abe85"} ,
"name" : "Firstname Secondname" ,
"reg_number" : "ATC/DCM/1016/230" ,
"oral" : 11 ,
"oral_percent" : 73 ,
"cat_1" : 57 ,
"cat_2" : 60 ,
"cat_average" : 59 ,
"assignment" : 90
}"
How do I map this in Kotlin Map/MutableMap? Is there an API in Kotlin to read JSON and map it to Map/MutableMap?
No additional library is needed:
val jsonObj = JSONObject(jsonString)
val map = jsonObj.toMap()
where toMap is:
fun JSONObject.toMap(): Map<String, *> = keys().asSequence().associateWith {
when (val value = this[it])
{
is JSONArray ->
{
val map = (0 until value.length()).associate { Pair(it.toString(), value[it]) }
JSONObject(map).toMap().values.toList()
}
is JSONObject -> value.toMap()
JSONObject.NULL -> null
else -> value
}
}
This can be done with Klaxon. With this you can easily read the Json data as JsonObject which is actually a MutableMap.
val json: JsonObject = Parser().parse(jsonData) as JsonObject
Using Jackson's kotlin module, you can create a Map/MutableMap as below:
val jsonString = "{\n" +
" \"_id\": {\n" +
" \"\$oid\": \"593440eb7fa580d99d1abe85\"\n" +
" },\n" +
" \"name\": \"Firstname Secondname\",\n" +
" \"reg_number\": \"ATC/DCM/1016/230\",\n" +
" \"oral\": 11,\n" +
" \"oral_percent\": 73,\n" +
" \"cat_1\": 57,\n" +
" \"cat_2\": 60,\n" +
" \"cat_average\": 59,\n" +
" \"assignment\": 90\n" +
"}"
val map = ObjectMapper().readValue<MutableMap<Any, Any>>(jsonString)
Note: In case you're getting the below compilation error
None of the following functions can be called with the arguments supplied
Please ensure that you have added the the dependency of jackson-module-kotlin (for gradle: compile "com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}") and have added the import for the readValue implementation as import com.fasterxml.jackson.module.kotlin.readValue in the place where you're using readValue
This can be done without any third party library:
#Throws(JSONException::class)
fun JSONObject.toMap(): Map<String, Any> {
val map = mutableMapOf<String, Any>()
val keysItr: Iterator<String> = this.keys()
while (keysItr.hasNext()) {
val key = keysItr.next()
var value: Any = this.get(key)
when (value) {
is JSONArray -> value = value.toList()
is JSONObject -> value = value.toMap()
}
map[key] = value
}
return map
}
#Throws(JSONException::class)
fun JSONArray.toList(): List<Any> {
val list = mutableListOf<Any>()
for (i in 0 until this.length()) {
var value: Any = this[i]
when (value) {
is JSONArray -> value = value.toList()
is JSONObject -> value = value.toMap()
}
list.add(value)
}
return list
}
Usage to convert JSONObject to Map:
val jsonObject = JSONObject(jsonObjStr)
val map = jsonObject.toMap()
Usage to convert JSONArray to List:
val jsonArray = JSONArray(jsonArrStr)
val list = jsonArray.toList()
More info is here
This is now also possible with kotlinx.serialization:
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
val input = """{
"_id" : { "some_id" : "593440eb7fa580d99d1abe85"} ,
"name" : "Firstname Secondname" ,
"reg_number" : "ATC/DCM/1016/230" ,
"oral" : 11 ,
"oral_percent" : 73 ,
"cat_1" : 57 ,
"cat_2" : 60 ,
"cat_average" : 59 ,
"assignment" : 90
}"""
val json = Json.parseToJsonElement(input)
val map = json.jsonObject.toMap()
The latest version of the org.json library has JSONObject.toMap() so you can just do
val map = JSONObject(string).toMap()
So just add the org.json:json:20220924 dependency to your project.
If you're writing for Android (and thus have an old version of the org.json library) then the following can be used.
import org.json.JSONArray
import org.json.JSONObject
fun JSONObject.toMap(): Map<String, Any?> =
keys().asSequence().associateWith { key -> toValue(get(key)) }
fun JSONArray.toList(): List<Any?> =
(0 until length()).map { index -> toValue(get(index)) }
private fun toValue(element: Any) = when (element) {
JSONObject.NULL -> null
is JSONObject -> element.toMap()
is JSONArray -> element.toList()
else -> element
}

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