I have the following code, I receive the following exception when I deserilize the response to a Gson from JSON:
java.lang.ClassCastException: com.test.model.photos.photosModel cannot be cast to androidx.lifecycle.MutableLiveData
This is my My code when I receive the response from the server:
lateinit var _photosModel:MutableLiveData<photosModel>
val gson = Gson()
val modelObj = gson.fromJson<Any>(response.toString(), photosModel::class.java)
_photosModel = modelObj as MutableLiveData<photosModel>
Simply because you are parsing from Json to model is Any that while you are needing type MutableLiveData.
And more thing, if you want to set data for MutableLiveData, you should use 2 methods setValue() if you want to set value in main thread and postValue() if you want to set value in other thread.
Just like that:
YourViewModel.kt
private val _photosModel = MutableLiveData<PhotosModel>()
val photosModel: LiveData<PhotosModel> = _photosModel
val gson = Gson()
val modelObj: PhotosModel = gson.fromJson(response.toString(), PhotosModel::class.java) // You should make the first letter of the class uppercase.
// Set value for mutable livedata
_photosModel.value = modelObj
Related
In Gson I can do something like this:
//mapItem - item of map with info for which class should string be deserialized
BaseReqClass req = (BaseReqClass) gson.fromJson(jsonString, mapItem.getValue());
req.doSomeStuff()
where
class AReq : BaseReqClass()
class BReq : BaseReqClass()
I'm selecting mapItem based on map key, which also clearly define me a subclass which moshi should istantiate (mapItem.getValue()).
What should I do to get the same behavior with Moshi?
I know that it is PolymorphicJsonAdapterFactory but I don’t want to have a special field in my json.
Ok, I found solution.
I know what should I do because of mapItem.
So I've implemented something like this:
val rsp =
"{\"token\":\"ABC\",\"data\":{\"id\":\"1234\",\"hash\":\"9N6PpUW9H8T6tuEc1wcvWu\"},\"locale\":\"EN\"}";
//val rsp = "{\"token\":\"FGH\",\"data\":{\"somefield\":\"someString\",\"otherid\":\"555\"},\"locale\":\"EN\"}"
val factory: JsonAdapter.Factory
if (getServiceName() == "areq") {
factory = ResultJsonAdapterFactory.of(BaseReqClass::class.java)
.withSubtype(AReq::class.java)
} else {
factory = ResultJsonAdapterFactory.of(BaseReqClass::class.java)
.withSubtype(BReq::class.java)
}
val moshi = Moshi.Builder()
.add(factory)
.build()
val jsonAdapter = moshi.adapter(BaseReqClass::class.java)
val req = jsonAdapter.fromJson(rsp)
println("serialized: ${jsonAdapter.toJson(req)}")
I can swap rsp from comment on top and moshi deserialize data to second class object.
I have an JSONObject which is creating in the following way.
For a special reason, I have to convert it to Gson in this way, as i can modify only extractDataToString() method. But I do not get proper value after conversion.
fun createJsonString() {
val headerJson = JSONObject()
headerJson.put("typ1", "value1")
headerJson.put("typ2", "value2")
extractData(headerJson.toString())
}
fun extractDataToString(jsonString: String) {
val headerJson = Gson().fromJson(jsonString, JsonObject::class.java)
val resultType1 = headerJson.remove("typ1")?.toString() // Here, resultType1 becomes
"value1", but it should be
value1 that is extra qutomation
mark is being added.
}
Why extra quotation mark is being added? Can anyone please help? I am a newbie.
Call
.getAsString()
Instead of
toString()
at the end
ref : docs
I wrote a Java App where data was retrieved from an API in JSON. Depending on the endpoint the actual data could be very divers. So I made a function to convert the data to the required class for every possible class. This gives me multiple functions which only differ in the classtype to convert to. Following are two examples:
public List<ZermeloBranch> processBranches(ZermeloApiResponseObject responseObject) {
List<ZermeloBranch> branches = new ArrayList<>();
List<LinkedTreeMap> branchMaps = responseObject.getData();
Gson gson = new Gson();
for (LinkedTreeMap treeMap : branchMaps) {
branches.add(gson.fromJson(gson.toJsonTree(treeMap).toString(), ZermeloBranch.class));
}
return branches;
}
public List<ZermeloAnnouncement> processAnnouncements(ZermeloApiResponseObject responseObject) {
List<ZermeloAnnouncement> announcements = new ArrayList<ZermeloAnnouncement>();
List<LinkedTreeMap> announcementMaps = responseObject.getData();
Gson gson = new Gson();
for (LinkedTreeMap treeMap : announcementMaps) {
announcements.add(gson.fromJson(gson.toJsonTree(treeMap).toString(), ZermeloAnnouncement.class));
}
return announcements;
}
Now I am rewriting this app to Kotlin and I summise it should be possible to write one function to process the data passing the class to decode to as a parameter. So I made both ZermeloBranch and ZermeloAnnouncement inherit from ZermeloDataObject. I would like to write one function like this:
fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):List<KClass<out ZermeloDataObject>>{
val returnList:ArrayList<KClass<out ZermeloDataObject>> = arrayListOf()
val gson = Gson()
for (item in 0 until data.length()){
returnList.add(gson.fromJson(item, convertToClass))
}
return returnList
}
and call it with processDataToList(data, ZermeloAnnouncements::class) and get a List<ZermeloAnnoucement> returned of call it with processDataToList(data, ZermeloBranch::class) and get a List<ZermeloBranch> returned.
Alas, the compiler gives me an error on gson.fromJson stating "None of the following functions can be called with the arguments supplied" and then it lists all possible functions.
Is it possible to use one function as I propose, and if so, what am I doing wrong?
You could use TypeToken provided by Gson to correctly handle the return type. Besides this you are passing index instead of data.
val type = object: TypeToken<KClass<out ZermeloDataObject>>() {}.type
fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):ArrayList<KClass<out ZermeloDataObject>>{
val returnList:ArrayList<KClass<out ZermeloDataObject>> = arrayListOf()
val type = object: TypeToken<KClass<out ZermeloDataObject>>() {}.type
val gson = Gson()
for (item in 0 until data.length()){
returnList.add(gson.fromJson(data[item].toString(), type))
}
return returnList
}
Instead of this you could do the whole thing in a single line of code. Check below:
fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):List<KClass<out ZermeloDataObject>>{
return Gson().fromJson(data.toString(), object: TypeToken<List<KClass<out ZermeloDataObject>>>() {}.type)
}
Beside this, you can also use below style which is more generic:
fun <T> processDataToList(data:JSONArray): List<T> {
val type = object: TypeToken<T>() {}.type
val returnList: ArrayList<T> = arrayListOf()
val gson = Gson()
for (item in 0 until data.length()) {
returnList.add(gson.fromJson(json[item].toString(), type))
}
return returnList
}
Or Simply
fun <T> processDataToList(data:JSONArray):List<T>{
return Gson().fromJson(data.toString(), object: TypeToken<List<T>>() {}.type)
}
I'm really new in programming, and recently started a project in Kotlin with Android Studio.
So, I have a problem with a JSON object. I get data from an BroadcastReceiver object, a String to be more specific, with the next format:
{"s1":1}
This, is a simple string. So I took in a function call toJson and I do this.
private fun toJson(data:String): JSONObject {
var newData: String = data.replace("\"","")
newData = newData.replace("{","")
newData = newData.replace("}","")
val newObject = newData.split(":")
val name = newObject[0]
val value = newObject[1]
val rootObject = JSONObject()
rootObject.put(name,value)
return rootObject
}
Im doing this the right way?, how can I improve my code?
Thanks for your help, and sorry for my english!
Welcome to StackOverflow!
In 2019 no-one is really parsing JSON manually. It's much easier to use Gson library. It takes as an input your object and spits out JSON string and vice-versa.
Example:
data class MyClass(#SerializedName("s1") val s1: Int)
val myClass: MyClass = Gson().fromJson(data, MyClass::class.java)
val outputJson: String = Gson().toJson(myClass)
This way you're not working with JSON string directly but rather with Kotlin object which is type-safe and more convenient.
Look at the docs. It's pretty big and easy to understand
Here is some tutorials:
https://www.youtube.com/watch?v=f-kcvxYZrB4
http://www.studytrails.com/java/json/java-google-json-introduction/
https://www.tutorialspoint.com/gson/index.htm
UPDATE: If you really want to use JSONObject then use its constructor with a string parameter which parses your JSON string automatically.
val jsonObject = JSONObject(data)
Best way is using kotlinx.serialization. turn a Kotlin object into its JSON representation and back marking its class with the #Serializable annotation, and using the provided encodeToString and decodeFromString<T> extension functions on the Json object:
import kotlinx.serialization.*
import kotlinx.serialization.json.*
#Serializable
data class User(val name: String, val yearOfBirth: Int)
// Serialization (Kotlin object to JSON string)
val data = User("Louis", 1901)
val string = Json.encodeToString(data)
println(string) // {"name":"Louis","yearOfBirth":1901}
// Deserialization (JSON string to Kotlin object)
val obj = Json.decodeFromString<User>(string)
println(obj) // User(name=Louis, yearOfBirth=1901)
Further examples: https://blog.jetbrains.com/kotlin/2020/10/kotlinx-serialization-1-0-released/
I am adding 3 templates here for Kotlin Developers, It will solve json converting & parsing problems.
//Json Array template
{
"json_id": "12.4",
"json_name": "name of the array",
"json_image": "https://image_path",
"json_description": "Description of the Json Array"
}
Kotlin Model class
data class JsonDataParser(
#SerializedName("json_id") val id: Long,
#SerializedName("json_name") val name: String,
#SerializedName("json_image") val image: String,
#SerializedName("json_description") val description: String
)
Converting to Json String from the Model Class
val gson = Gson()
val json = gson.toJson(jsonDataParser)
Parsing from Json file/Strong
val json = getJson()
val topic = gson.fromJson(json, JsonDataParser::class.java)
I use this class to store data
public class Item(var name:String,
var description:String?=null){
}
And use it in ArrayList
public var itemList = ArrayList<Item>()
Use this code to serialize the object
val gs=Gson()
val itemListJsonString = gs.toJson(itemList)
And deserialize
itemList = gs.fromJson<ArrayList<Item>>(itemListJsonString, ArrayList::class.java)
But this method will give me LinkedTreeMap, not Item, I cannot cast LinkedTreeMap to Item
What is correct way to deserialize to json in Kotlin?
Try this code for deserialize list
val gson = Gson()
val itemType = object : TypeToken<List<Item>>() {}.type
itemList = gson.fromJson<List<Item>>(itemListJsonString, itemType)
You can define a inline reified extension function like:
internal inline fun <reified T> Gson.fromJson(json: String) =
fromJson<T>(json, object : TypeToken<T>() {}.type)
And use it like:
val itemList: List<Item> = gson.fromJson(itemListJsonString)
By default, types are erased at runtime, so Gson cannot know which kind of List it has to deserialize. However, when you declare the type as reified you preserve it at runtime. So now Gson has enough information to deserialize the List (or any other generic Object).
In my code I just use:
import com.google.gson.Gson
Gson().fromJson(string_var, Array<Item>::class.java).toList() as ArrayList<Type>
I give here a complete example.
First the type and the list array:
class Item(var name:String,
var description:String?=null)
var itemList = ArrayList<Item>()
The main code:
itemList.add( Item("Ball","round stuff"))
itemList.add(Item("Box","parallelepiped stuff"))
val striJSON = Gson().toJson(itemList) // To JSON
val backList = Gson().fromJson( // Back to another variable
striJSON, Array<Item>::class.java).toList() as ArrayList<Item>
val striJSONBack = Gson().toJson(backList) // To JSON again
if (striJSON==striJSONBack) println("***ok***")
The exit:
***OK***
Instead of the accepted answer (that works but creates an object to get its type), you could just do:
val gson = Gson()
itemList = gson.fromJson(itemListJsonString, Array<Item>::class.java)
There "Array" represents a Java array when targeting the JVM platform. That's not an ArrayList but you can access the items (that's usually all is needed after parsing JSON).
If you still need to manipulate the list you could easily convert it to mutable by doing:
itemsList.toMutableList()