I have a string separated by ";" like this:
var dataString: String = "Juan;25;Argentina"
And I want to convert it to this sample data class:
data class Person (
var name: String? = "",
var age: Int? = "",
var location: String? = "")
The only way I figured out to that is like this:
var dataString: String = "Juan;25;Argentina"
var fieldList = dataString.split(";").map { it.trim() }
var personItem = Person(fieldList[0], fieldList[1], fieldList[2])
But this is too static, if I then have to add new field to Person data class, I have to manually add fieldList[3]
Which is the most efficient way to do this? (I searched a lot, but couldn't find it :S )
SOLUTION:
//Data in string
val rawData = "name;age;location\nPerson1;20;USA\nPerson2;30;Germany"
//Custom string reader with ";" separator
val customCSV = csvReader { delimiter = ';' }
//String to map list
val parsed = csvReader().readAllWithHeader(rawData)
//Mal list to list of <Person>
val finalData = grass<Person>().harvest(parsed)
for (entry in entryList) {
//entry is an instance of Person
}
EDIT2:
You can obtain field names for header line like this:
var headerLine = ""
for (entry in Person::class.members) {
if (member.name.isNotEmpty())
headerLine += ";${member.name}"
}
headerLine.substring(1)
The string you showed here looks like a CSV dataset. So I think you could use kotlin-grass and kotlin-csv
https://github.com/blackmo18/kotlin-grass
https://github.com/doyaaaaaken/kotlin-csv/
Example:
val rawData = "Person1;20;USA/nPerson1;30;Germany"
val parsed = csvReader().readAllWithHeader(rawData)
val finalData = grass<Person>().harvest(parsed)
Related
I'm very new to Kotlin and Android. I have created an array that accepts input from EditText of 10 values separated by commas, however It is a String array and I need a Int array. Any Help would be very Appreciated. Below is my code:
private fun array() {
val ed = findViewById<View>(R.id.et_addNum) as EditText
var text = ed.text.toString()
val array: List<String> = text.split(",")
for (element in array) {
Log.e("elements", element)
storednumber.setText(array.toString())
}
}
If you absolutely sure your Strings are correct numbers you can use mapping like this:
val stringList: List<String> = listOf("1", "2", "3")
val intList: List<Int> = stringList.map { it.toInt() }
or like this if want to avoid throwing NumberFormatException:
val stringList: List<String> = listOf("1", "2", "3")
val intList: List<Int> = stringList.map { it.toIntOrNull() ?: 0 }
This is my string:
{"array":[{"message":"test1","name":"test2","creation":"test3"},{"message":"test1","name":"test2","creation":"test3"}]}
And I want it get that array into a list of object in Kotlin app for Android.
I tried to do it using two examples from this site... So here is my code (res = that string):
val gson = Gson()
val obj = gson.fromJson(res, JsonObject::class.java)
val arr = obj.getAsJsonArray("array")
println(arr.toString())
val list1 : List<JThread> = gson.fromJson(arr, object : TypeToken<List<JThread>>() {}.type)
val list2 = gson.fromJson(arr, Array<JThread>::class.java).asList()
for (x in list1){
println(x.message)
}
for (x in list2){
println(x.message)
}
However I'm only getting null in x.message. I don't know what can go wrong.
I also tried changing arr to arr.toString() everywhere and that didn't work either.
Also JThread is:
object JThread {
var message: String? = null
var name: String? = null
var creation: String? = null }
This can be done without GSON or any other 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 JSONArray to List:
val jsonArray = JSONArray(jsonArrStr)
val list = jsonArray.toList()
Usage to convert JSONObject to Map:
val jsonObject = JSONObject(jsonObjStr)
val map = jsonObject.toMap()
More info is here
Use this code:
import com.google.gson.annotations.SerializedName
import com.google.gson.Gson
data class Array(
#SerializedName("message")
var message: String,
#SerializedName("name")
var name: String,
#SerializedName("creation")
var creation: String
)
data class Example(
#SerializedName("array")
var array: List<Array>? = null
)
private fun fromJson(json:String):Example{
return Gson().fromJson<Example>(json, Example::class.java)
}
PS: I made it with this site:http://www.jsonschema2pojo.org/
I have one issue about code data class kotlin android.
How to implement server response? sometimes I get String value or sometime get Object class.
class CMSRespTemp {
data class CMSRespApi(
val status: Boolean = false,
val message: String = "",
val data: String as Data
)
data class Data(
val cms_id: String = "",
val cms_content: String = ""
)
}
When I implement only Data class it works, like this val data: Data or val data: String. But I need together Data and String with key only data.
Is it possible?
When having multiple type for same variable, we can use Any type which is equivalent to Object type in java. So solution is like below :
class CMSRespTemp {
data class CMSRespApi(
val status: Boolean = false,
val message: String = "",
var data: Any? = null // changed it to var from val, so that we can change it's type runtime if required
)
data class Data(
val cms_id: String = "",
val cms_content: String = ""
)
}
And when accessing that variable, one can simply cast like below :
val apiResponse : CMSRespApi //= some API response here from network call
when (apiResponse.data) {
is String -> {
// apiResponse.data will be smart-casted to String here
}
else -> {
val responseData = Gson().fromJson<CMSRespApi.Data>(
Gson().toJsonTree(apiResponse.data),
CMSRespApi.Data::class.java
)
}
}
After 12 Hrs spend and got the solution my self,
val getResultCon = getSerCont.result // response Any
val gson = Gson()
val jsonElement = gson.toJsonTree(getResultCon)
val resultData = gson.fromJson(jsonElement, SearchContactApi.Result::class.java)
Convert your data string to toJsonTree and fromJson with model class then got result.
I have following groups of string and would like to create a string following the syntax: substr1.substr2.substr3 = substr1 + substr2 + substr3
data class Protocol( val http: String = "http://",
val https: String = "https://")
data class BaseUrl( val baseProd: String = "url_prod",
val baseDev: String = "url_dev",
val baseTest: String = "url_test")
data class ApiEndpoint( val GET_USER: String = "endpoint_get_user",
val LOGIN: String = "endpoint_login",
val REGISTER: String = "endpoint_get",
val FETCH_HISTORY: String = "endpoint_fetch_history")
data class WebUrl( val HOME: String = "path_home",
val BALANCE: String = "path_balance",
val MANAGE_SUBSCRIPTION: String = "path_manage_subscription")
data class RequestEnvironment( val mobile: String = "query_mobile",
val desktop: String = "query_desktop")
My goal is to make something that build the strings like this
UrlFactory.https.baseDev.GET_USER //result: https://url_dev/get_user
UrlFactory.https.baseProd.HOME.mobile //result: https://url_prod/home?mobile=1
UrlFactory.http.baseDev.BALANCE //result: http://url_dev/balance
Have anyone built a nice way to handle url strings dynamically like this?
You can create an object Url which is in charge of building up your URL string.
You can build up Url by passing in your selected options through the constructor.
When the object is constructed, you can then call toString which will concatenate and return the values together.
class Url ( var protocol : Protocol,
var baseUrl : BaseUrl,
var apiEndpoint : ApiEndpoint,
var webUrl : WebUrl,
var requestEnvironment : RequestEnvironment) {
override fun toString() : String {
return protocol.value +
baseUrl.value +
apiEndpoint.value +
webUrl.value +
requestEnvironment.value
}
}
To add another level of safety when working with your Strings, I took the liberty of converting them to enums. This will give you the benefit of allowing you to limit the possible values which can be set:
enum class Protocol(val value : String) {
HTTP("http://"),
HTTPS("https://")
}
enum class BaseUrl(val value : String) {
BASE_PROD("url_prod"),
BASE_DEV("url_dev"),
BASE_TEST("url_test")
}
enum class ApiEndpoint(val value : String) {
GET_USER("endpoint_get_user"),
LOGIN("endpoint_login"),
REGISTER("endpoint_get"),
FETCH_HISTORY("endpoint_fetch_history")
}
enum class WebUrl(val value : String) {
HOME("path_home"),
BALANCE("path_balance"),
MANAGE_SUBSCRIPTION("path_manage_subscription")
}
enum class RequestEnvironment(val value : String) {
MOBILE("query_mobile"),
DESKTOP("query_desktop")
}
Finally, here is an example of how you can now build your URL:
fun main() {
val url : Url = Url(Protocol.HTTP,
BaseUrl.BASE_DEV,
ApiEndpoint.GET_USER,
WebUrl.HOME,
RequestEnvironment.MOBILE);
println(url.toString())
}
I have these classes written in kotlin, Location, and the rest is in the Application.kt
#RealmClass
open class Location(
#PrimaryKey
#SerializedName("id")
var id: Int = 0,
#SerializedName("city_name")
var city_name: String? = null,
#SerializedName("elevation")
var elevation: Int = 0,
#SerializedName("state_code")
var state_code: String? = null,
#SerializedName("state_name")
var state_name: String? = null,
#SerializedName("country_code")
var country_code: String? = null,
#SerializedName("country_name")
var country_name: String? = null
):RealmObject()
and the rest:
private fun loadStuff() {
val inputStream = this.resources.openRawResource(R.raw.city_json)
val jsonReader = JsonReader(InputStreamReader(inputStream, "UTF-8"))
val gson = Gson()
Realm.getDefaultInstance().executeTransactionAsync(Realm.Transaction { realm ->
val weatherList = gson.fromJson<List<Location>>(jsonReader , Array<Location>::class.java).toList()
//realm.insertOrUpdate(location)
jsonReader.endArray()
jsonReader.close()
}, Realm.Transaction.OnSuccess {
Log.d("TAG", "Success")
})
}
and I keep getting exception:
com.example.android.sunshine.data.Location[] cannot be cast to java.lang.Iterable
what am I doing wrong ?
the object looks like this:
[
{
"id":3040051,
"city_name":"les Escaldes",
"elevation":0,
"state_code":"08",
"state_name":"ParrĂ²quia d'Escaldes-Engordany",
"country_code":"AD",
"country_name":"Andorra"
},
{
"id":3041563,
"city_name":"Andorra la Vella",
"elevation":0,
"state_code":"07",
"state_name":"ParrĂ²quia d'Andorra la Vella",
"country_code":"AD",
"country_name":"Andorra"
}
]
This:
List<Location>
Is a List of Location. List implements Iterable.
This:
Array<Location>
is an Array of Location. Array does not implement Iterable.
The differences are bigger than that, but his is the one your error is for.
It was enough to swap List with Array and remove .toList() and it worked like magic