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()
Related
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
In my application, I use this class as a model:
class ExpenseItem (val concept: String, val amount: String, val months: List<String>, val type: Type, val cards_image: Int, val payDay: Int, val notes: String) {
enum class Type {RECURRENT, VARIABLE}
}
And with this model, I create a mutable list
var generalExpensesList: MutableList<ExpenseItem> = mutableListOf()
and I add items
val currentExpense = ExpenseItem(
concept,
amount,
listOfMonths,
enumtype,
card_image_number,
payday.toInt(),
notes
)
generalExpensesList.add(currentExpense)
As you can see, one of the model fields is also a String type list, in case it was important
Well, my intention is to convert this list to String, save it as a sharedpreference, and then create a new list using that String retrieved from the sharedpreference.
To convert the list to String I can use toString or joinToString, both give me an optimal result.
I have the problem when I want to create a new list from the String.
I can do it with lists of type List<String>, but never with lists of type List<ExpenseItem>
Can someone help me with this?
Simple way, you can use Gson library, add it to build.gradle, it will serialize your list to JSON and save it to SharePreference
implementation 'com.google.code.gson:gson:2.8.6'
public void saveItems(List<ExpenseItem> items) {
if (items != null && !items.isEmpty()) {
String json = new Gson().toJson(items);
mSharedPreferences.edit().putString("items", json).apply();
}
}
public List<ExpenseItem> getItems() {
String json = mSharedPreferences.getString("items", "");
if (TextUtils.isEmpty(json)) return Collections.emptyList();
Type type = new TypeToken<List<ExpenseItem>>() {
}.getType();
List<ExpenseItem> result = new Gson().fromJson(json, type);
return result;
}
You need to use Gson() class.
First Make class like below
class WhateverName(var generalExpensesList: MutableList<ExpenseItem> = mutableListOf())
and pass your list to this class and make a object of this class and then need to make string of it like below
Gson().toJson(WhateverName(arrayListOf()))
it will give you the string and save it to preference as a string.
After retrieve string from preference you need convert it to that object again for doing that use below code.
Gson().fromJson("string", WhateverName::class.java)
it will give you the class of WhateverName, you can access your list from it.
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'm trying to convert a HashMap of elements into a JSON string. I'm using the method used in this link.
val elementsNew: HashMap<String, Element> = HashMap(elements)
val type = Types.newParameterizedType(Map::class.java, String::class.java, Element::class.java)
var json: String = builder.adapter(type).toJson(elementsNew)
But this gives the following error
Error:(236, 40) Type inference failed: Not enough information to infer
parameter T in fun adapter(p0: Type!): JsonAdapter!
Please specify it explicitly.
Can any one tell me where's the fault? Is it because of Kotlin?
Looking at the signature of the adapter() method, it can't infer its type parameter from the argument:
public <T> JsonAdapter<T> adapter(Type type)
Hence you have to provide the type explicitly:
var json = builder.adapter<Map<String, Element>>(type).toJson(elementsNew)
or alternatively:
val adapter: JsonAdapter<Map<String, Element>> = builder.adapter(type)
var json = adapter.toJson(elementsNew)