Gson does not use deserialization for generic type - kotlin

I try to parse this json {"value": [1, "a", "b"]} to triple object. Gson doesn't even call my deserialize.
class TripleDeserializer: JsonDeserializer<Triple<Int, String, String>> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): Triple<Int, String, String> {
val f = json!!.asJsonArray.get(0).asInt
val s = json.asJsonArray.get(1).asString
val t = json.asJsonArray.get(2).asString
return Triple(f, s, t)
}
}
class SomeClass(val value: Triple<Int, String, String>)
fun another() {
val input = "{\"value\": [1, \"a\", \"b\"] }"
val type = object : TypeToken<Triple<Int, String, String>>() {}.type
val gson = GsonBuilder()
.registerTypeAdapter(type, TripleDeserializer())
.create()
val out = gson.fromJson(input , SomeClass::class.java)
}

Related

Issue in Type Converter of Room Database Kotlin

Type Convertor Class :
class ProductTypeConvertor {
var gson = Gson()
#TypeConverter
fun foodRecipeToString(foodRecipe: ProductList): String {
return gson.toJson(foodRecipe)
}
#TypeConverter
fun stringToFoodRecipe(data: String): ProductList {
val listType = object : TypeToken<ProductList>() {}.type
return gson.fromJson(data, listType)
}
#TypeConverter
fun resultToString(result: Products): String {
return gson.toJson(result)
}
#TypeConverter
fun stringToResult(data: String): Products {
val listType = object : TypeToken<Products>() {}.type
return gson.fromJson(data, listType)
}
#TypeConverter
fun stringToVListServer(data: String?): List<Variants?>? {
if (data == null) {
return Collections.emptyList()
}
val listType: Type = object :
TypeToken<List<Variants?>?>() {}.type
return gson.fromJson<List<Variants?>>(data, listType)
}
#TypeConverter
fun VlistServerToString(someObjects: List<Variants?>?): String? {
return gson.toJson(someObjects)
}
#TypeConverter
fun stringToListServer(data: String?): List<String?>? {
if (data == null) {
return Collections.emptyList()
}
val listType: Type = object :
TypeToken<List<String?>?>() {}.type
return gson.fromJson<List<String?>>(data, listType)
}
#TypeConverter
fun listServerToString(someObjects: List<String?>?): String? {
return gson.toJson(someObjects)
}
}
Product Entity :
#ColumnInfo(name = "other_images")
var other_images: ArrayList<String> = arrayListOf(),
#ColumnInfo(name = "variants")
var variants : ArrayList<Variants> = arrayListOf()
Error :
error: incompatible types: List cannot be converted to ArrayList
_tmpVariants = __productTypeConvertor.stringToVListServer(_tmp_3);
error: incompatible types: List cannot be converted to ArrayList
_tmpOther_images = __productTypeConvertor.stringToListServer(_tmp);
The types do no match it's like the anecdotal apples(List type) and oranges(ArrayList type).
Either use ArrayList or List as both
a) the column type and
b) as the input parameter and the result from the converters.

TypeConverter not working for android Room DB

So I am trying to save a list of string pairs into my database but having some issues with the TypeConverter, I tried following guides ans other SO posts but can't figure out whats wrong...
My Entity:
#Entity
data class Credential(
#PrimaryKey()
val id: String,
#ColumnInfo(name = "name")
val name: String,
#ColumnInfo(name = "url")
val url: String?,
#TypeConverters(ListPairTypeConverter::class)
#ColumnInfo(name = "fields")
val fields: List<Pair<String, String>>
)
My Type Converter:
class ListPairTypeConverter {
#TypeConverter
fun storedStringToPairList(value: String): List<Pair<String, String>> {
return value.split("~!!!!!~").map {
val vals = it.split("!~~~~~!")
Pair(vals[0], vals[1])
}
}
#TypeConverter
fun pairListToStoredString(pl: List<Pair<String, String>>): String {
return pl.joinToString(separator = "~!!!!!~") { it.first + "!~~~~~!" + it.second }
}
}
My Error:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private final java.util.List<kotlin.Pair<java.lang.String, java.lang.String>> fields = null;
^
You are adding type converter at wrong place. instead of
#TypeConverters(ListPairTypeConverter::class)
#ColumnInfo(name = "fields")
val fields: List<Pair<String, String>>
You need to add at here
#Database(entities = [User::class], version = 1)
#TypeConverters(ListPairTypeConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}

mutableListOf to Bundle (Kotlin)

I have a mutableLIst:
var books = mutableListOf<Book>()
model "Book" is:
data class Book(val title: String, val id: Int)
My code is:
button2.setOnClickListener{
val delFragment = DelFragment()
val booksforDel = Bundle()
booksforDel.putStringArrayList("books", books as ArrayList<String>)
delFragment.setArguments(booksforDel)
val manager = supportFragmentManager
delFragment.show(manager,"Delete Book")
}
in Fragment I try to get data:
val booksForDelete = getArguments()?.getStringArrayList("books")!!
And get Error:
java.lang.ArrayStoreException: source[0] of type com.example.http_example.model.Book cannot be stored in destination array of type java.lang.String[]
How send a data from mutableList "books" to Bundle in DialogFragment?
You can implement Parcelable interface:
data class Book(val title: String, val id: Int) : Parcelable {
constructor(source: Parcel) : this(
source.readString()!!,
source.readInt()
)
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
writeString(title)
writeInt(id)
}
companion object {
#JvmField
val CREATOR: Parcelable.Creator<Book> = object : Parcelable.Creator<Book> {
override fun createFromParcel(source: Parcel): Book = Book(source)
override fun newArray(size: Int): Array<Book?> = arrayOfNulls(size)
}
}
}
And use it like the following:
var books = mutableListOf<Book>()
val booksforDel = Bundle()
booksforDel.putParcelableArray("books", books.toTypedArray())
Ann to retrieve books in a Fragment:
val booksForDelete = arguments?.getParcelableArray("books")

Kotlin vert.x parsing a JSON String to a Data class using gson always returns null

I am just playing around with vert.x 3.5.3 Kotlin and I am unable to parse a JSON string into a Data class using gson.
Here is the code
class DataVerticle : AbstractVerticle() {
override fun start(startFuture: Future<Void>) {
data class Product(
#SerializedName("id") val id: Int,
#SerializedName("name") val name: String,
#SerializedName("productCode") val productCode: String
)
val products: MutableList<Product> = mutableListOf()
val gson = Gson()
val eventBus = vertx.eventBus()
eventBus.consumer<String>("data.verticle") {
when (it.headers().get("ACTION")) {
"ADD_PRODUCT" -> {
val prodJson = it.body()
if (prodJson != null) {
println(prodJson)
val product = gson.fromJson(prodJson, Product::class.java)
println(product)
it.reply("SUCCESS")
}
}
else -> {
print("ERROR")
}
}
}
startFuture.complete()
}
}
The Problem is the parsed value is always null
Here is my sample json ->
{"id":1,"name":"SOAP","productCode":"P101"}
The json string sent over the eventBus is not null.
I am using this package for gson
com.google.code.gson', version: '2.8.5'
Thanks
You declare your class inside the method body, which Gson doesn't like much.
Extracting it to be nested class will work just fine:
class DataVerticle : AbstractVerticle() {
override fun start(startFuture: Future) {
val gson = Gson()
val eventBus = vertx.eventBus()
eventBus.consumer<String>("data.verticle") {
when (it.headers().get("ACTION")) {
"ADD_PRODUCT" -> {
val prodJson = it.body()
if (prodJson != null) {
println(prodJson)
val product = gson.fromJson(prodJson, Product::class.java)
println(product)
it.reply("SUCCESS")
}
}
else -> {
print("ERROR")
}
}
}
startFuture.complete()
}
data class Product(
#SerializedName("id") val id: Int,
#SerializedName("name") val name: String,
#SerializedName("productCode") val productCode: String
)
}
Tested with:
val vertx = Vertx.vertx()
vertx.deployVerticle(DataVerticle()) {
val options = DeliveryOptions()
options.addHeader("ACTION", "ADD_PRODUCT")
vertx.eventBus().send("data.verticle", """{"id":1,"name":"SOAP","productCode":"P101"}""", options)
}

Kotlin cannot object before array

I got this error when I am trying to get json array from JSONObject ArrivedResult .
Here is my json :
{
"ArrivedResult": {
"arrivals": [
{
"ident": "MSR637",
"aircrafttype": "A321",
"actualdeparturetime": 1541399820,
"actualarrivaltime": 1541406652,
"origin": "HECA"
}
]
}
}
my code is
private fun handleJson(jsonString: String?) {
val jsonObj = JSONObject(jsonString)
val ArrivedResult = jsonObj.getJSONObject("ArrivedResult")
val jsonArray = JSONArray(ArrivedResult.get("arrivals").toString())
val list = ArrayList<FlightShdu>()
var x = 0
while (x < jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(x)
list.add(FlightShdu(
jsonObject.getString("aircrafttype"),
jsonObject.getString("destination")
))
x++
}
}
The error I got is Caused by: org.json.JSONException: No value for ArrivedResult
I'm not sure what library you're using to deserialize JSON, but if you have fallback to using another ones than it's pretty easy. Foe example with Klaxon:
// To parse the JSON, install Klaxon and do:
//
// val root = Root.fromJson(jsonString)
import com.beust.klaxon.*
private val klaxon = Klaxon()
data class Root (
#Json(name = "ArrivedResult")
val arrivedResult: ArrivedResult
) {
public fun toJson() = klaxon.toJsonString(this)
companion object {
public fun fromJson(json: String) = klaxon.parse<Root>(json)
}
}
data class ArrivedResult (
val arrivals: List<Arrival>
)
data class Arrival (
val ident: String,
val aircrafttype: String,
val actualdeparturetime: Long,
val actualarrivaltime: Long,
val origin: String
)
Or with kotlinx.serialization
// To parse the JSON, install kotlin's serialization plugin and do:
//
// val json = Json(JsonConfiguration.Stable)
// val root = json.parse(Root.serializer(), jsonString)
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.internal.*
#Serializable
data class Root (
#SerialName("ArrivedResult")
val arrivedResult: ArrivedResult
)
#Serializable
data class ArrivedResult (
val arrivals: List<Arrival>
)
#Serializable
data class Arrival (
val ident: String,
val aircrafttype: String,
val actualdeparturetime: Long,
val actualarrivaltime: Long,
val origin: String
)
Please note that in both cases I have top-leve Root class, which is needed to unwrap top-level {} object from your example