Why LinkedTreeMap is generated after deserializing to another class using Gson? - android

I have JSON structure like following:
{
"status": 0,
"error_message": null,
"data": {
"1": {
"7": [
{
"id": "1",
"person_id": "3",
"friend_id": "2"
}
]
}
}
As you've noticed, the number of indexes(1, 7) are dynamic, I mean, they can be more. So, to parse this JSON, I decided to use Map. 7 can be used as a key, when id, person_id, friend_id can be a class named, for example, Model. So, firstly, I tried to get 1 from data in JSON and convert it to Map as I described above. Here is how I did it:
val data = myJson.getAsJsonObject("data").get("1")
val gson = Gson()
val type = object : TypeToken<Map<String, ArrayList<Model>>>() {
}.type
val myMap: Map<String, ArrayList<Model>> = gson.fromJson(data, type)
Everything was alright, until I referred to some field of my Model. Here is how I did it:
First of all I tried to refer to myMap as following:
As you see, instead of getting Model, there are list of LinkedTreeMap. So, when I refer to the field of Model, I got CastException.
So, how to solve this problem? I tried this not using TypeToken, but it didn't help.

you gave the wrong json one single closure "}" I think you've copied wrong
create Gson2Application class
class Gson2Application
val jj = "{\n" +
"\"status\": 0,\n" +
"\"error_message\": null,\n" +
"\"data\": {\n" +
"\"1\": {\n" +
" \"7\": [\n" +
" {\n" +
" \"id\": \"1\",\n" +
" \"person_id\": \"3\",\n" +
" \"friend_id\": \"2\"\n" +
" }\n" +
" ]\n" +
" }\n" +
"}\n" +
"}"
data class Model(val id: Long, val person_id: Long, val friend_id: Long)
fun main(args: Array<String>) {
var myJson = JsonParser().parse(jj).asJsonObject
val data = myJson.getAsJsonObject("data").get("1")
val type = object : TypeToken<Map<String, ArrayList<Model>>>() {}.type // the Map is interfacem then gson get default LinkedTreeMap extedn AbstractMap<K,V> implements Map
val fromMapDefault = Gson().fromJson<Map<String, List<Model>>>(data, type)
printResult(fromMapDefault)
// result
// {{7=[Model(id=1, person_id=3, friend_id=2)]} class com.google.gson.internal.LinkedTreeMap--> 1}
// {[Model(id=1, person_id=3, friend_id=2)] class java.util.ArrayList--> 1}
// {Model(id=1, person_id=3, friend_id=2) class pl.jac.gson2.Model --> 2}
// {1 long--> 3}
val typeHashMap = object : TypeToken<HashMap<String, ArrayList<Model>>>() {}.type // the HashMap is an implementation
val fromHashMap = Gson().fromJson<Map<String, List<Model>>>(data, typeHashMap)
printResult(fromHashMap)
//result
// {{7=[Model(id=1, person_id=3, friend_id=2)]} class java.util.HashMap--> 1}
// {[Model(id=1, person_id=3, friend_id=2)] class java.util.ArrayList--> 1}
// {Model(id=1, person_id=3, friend_id=2) class pl.jac.gson2.Model --> 2}
// {1 long--> 3}
}
private fun printResult(fromMapDefault: Map<String, List<Model>>) {
println("""
result
{${fromMapDefault} ${fromMapDefault?.javaClass}--> 1}
{${fromMapDefault["7"]} ${fromMapDefault["7"]?.javaClass}--> 1}
{${fromMapDefault["7"]?.get(0)} ${fromMapDefault["7"]?.get(0)?.javaClass} --> 2}
{${fromMapDefault["7"]?.get(0)?.id} ${fromMapDefault["7"]?.get(0)?.id?.javaClass}--> 3}
""".trimIndent())
}
}
private fun printResult(fromMapDefault: Map<String, List<Model>>) {
println("""
result
{${fromMapDefault} ${fromMapDefault?.javaClass}--> 1}
{${fromMapDefault["7"]} ${fromMapDefault["7"]?.javaClass}--> 1}
{${fromMapDefault["7"]?.get(0)} ${fromMapDefault["7"]?.get(0)?.javaClass} --> 2}
{${fromMapDefault["7"]?.get(0)?.id} ${fromMapDefault["7"]?.get(0)?.id?.javaClass}--> 3}
""".trimIndent())
}
i change to yours model ClassStatus
data class ClassStatus(val status: Int, val error_message: String?, val data: Map<String,Map<String, ArrayList<Model>>>)
data class Model(val id: Long, val person_id: Long, val friend_id: Long)
fun main(args: Array<String>) {
val fromJson = Gson().fromJson<ClassStatus>(jj, ClassStatus::class.java)
printResult(fromJson)
//result
//ClassStatus(status=0, error_message=null, data={1={7=[Model(id=1, person_id=3, friend_id=2)]}}) class pl.jac.gson2.not.ClassStatus--> 1
//{1={7=[Model(id=1, person_id=3, friend_id=2)]}} class com.google.gson.internal.LinkedTreeMap--> 2
//{7=[Model(id=1, person_id=3, friend_id=2)]} class com.google.gson.internal.LinkedTreeMap--> 3
//[Model(id=1, person_id=3, friend_id=2)] class java.util.ArrayList--> 4
//Model(id=1, person_id=3, friend_id=2) class pl.jac.gson2.not.Model--> 5
//1 long--> 6
}
private fun printResult(fromMapDefault: ClassStatus) {
println("""
result
${fromMapDefault} ${fromMapDefault.javaClass}--> 1
${fromMapDefault.data} ${fromMapDefault.data.javaClass}--> 2
${fromMapDefault.data["1"]} ${fromMapDefault.data["1"]?.javaClass}--> 3
${fromMapDefault.data["1"]!!["7"]} ${fromMapDefault.data["1"]!!["7"]!!.javaClass}--> 4
${fromMapDefault.data["1"]?.get("7")!![0]} ${fromMapDefault.data["1"]!!["7"]!![0].javaClass}--> 5
${fromMapDefault.data["1"]?.get("7")!![0].id} ${fromMapDefault.data["1"]!!["7"]!![0].id!!.javaClass}--> 6
""".trimIndent())
}
val jj = "{\n" +
"\"status\": 0,\n" +
"\"error_message\": null,\n" +
"\"data\": {\n" +
"\"1\": {\n" +
" \"7\": [\n" +
" {\n" +
" \"id\": \"1\",\n" +
" \"person_id\": \"3\",\n" +
" \"friend_id\": \"2\"\n" +
" }\n" +
" ]\n" +
" }\n" +
"}\n" +
"}"

Related

No way to get nextToken when using custom GraphQL query?[Amplify]

//MovieList function Defination
private fun getMovieList(): GraphQLRequest<MovieDBFinal2> {
val document = ("query getMovieList { "
+ "listMovieDBFinal2s(limit: 1000, filter: {Genre1: {eq: \"Animation\"}}) { "
+ "nextToken "
+ "items { "
+ "IMDB_title "
+ "} "
+ "} "
+ "} ")
return SimpleGraphQLRequest(
document,
Collections.emptyMap(),
TypeMaker.getParameterizedType(QueryResponse::class.java, MovieDBFinal2::class.java),
GsonVariablesSerializer())
}
internal class QueryResponse<T> {
private val listMovieDBFinal2s: QueryList<T>? = null
val moviesList: QueryList<T>?
get() = listMovieDBFinal2s
}
internal class QueryList<T> {
val items: Iterable<T>? = null
val nextToken: String? = null
}
//MovieList function call
Amplify.API.query(
getMovieList(),
{ response ->
if (response.hasData()) {
Log.e("MovieList", "$response")
}
},
{ failure ->
Log.e("MovieList", "Query failed.", failure)
}
)
I tried this type in my schema not working.Github issue
PaginationResult not working.Iterable also not giving any token. Only solution is String.class but that is difficult to serialize. The workaround is make the same request two times once with PaginationResult passing the nextToken as input and second make the same request with string.Class type.

how to check null parameter in room queries with joined tables?

I'm trying to get data using Room library with SQLite and kotlin with this query but I have 2 nullable parameters with the joined tables and I don't know how to check if they are null or not.
I want to see if they are null, then returns null and if they are not null returns the parameter but I am using this query RN and the problem is the query returns data only if all parameters (even nullable ones ) are not null.
this is my query
#Query(
"SELECT " +
"f.word AS from_word, " +
"f.word_id as from_word_id, " +
"f2.word AS from_word2, " +
"f2.word_id as from_word_id2, " +
"fl.language AS from_language, " +
"fl.language_id AS from_language_id, " +
"t.word AS to_word, " +
"t.word_id AS to_word_id, " +
"t2.word AS to_word2, " +
"t2.word_id AS to_word_id2, " +
"tl.language AS to_language, " +
"tl.language_id AS to_language_id, " +
"time, " +
"date, " +
"day_of_week AS dayOfWeek, " +
"country, " +
"city, " +
"bookmarked " +
"FROM word_translate_map " +
"JOIN word AS f ON from_word_map = f.word_id " +
"JOIN word AS t ON to_word_map = t.word_id " +
"JOIN word AS f2 ON from_word_map2 = f2.word_id " +
"JOIN word AS t2 ON to_word_map2 = t2.word_id " +
"JOIN language AS fl ON f.language_map = fl.language_id " +
"JOIN language AS tl ON t.language_map = tl.language_id "
)
abstract fun getAllTranslatedWords(): LiveData<List<TranslatedWord>>
and this is my table that I have a problem with it.
#Entity(
tableName = "word_translate_map",
primaryKeys = ["from_word_map","to_word_map"],
foreignKeys = [
ForeignKey(
entity = Word::class,
parentColumns = ["word_id"],
childColumns = ["from_word_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = Word::class,
parentColumns = ["word_id"],
childColumns = ["to_word_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
],
indices = [
Index("to_word_map")
]
)
data class WordTranslateMap(
#ColumnInfo(name = "from_word_map")
val fromWord: Long,
#ColumnInfo(name = "to_word_map")
val toWord: Long,
#ColumnInfo(name = "from_word_map2")
val fromWord2: Long?,
#ColumnInfo(name = "to_word_map2")
val toWord2: Long?,
#ColumnInfo(name = "time")
val time: String,
#ColumnInfo(name = "date")
val date: String,
#ColumnInfo(name = "country")
val country: String,
#ColumnInfo(name = "city")
val city: String,
#ColumnInfo(name = "day_of_week")
val dayOfWeek: String,
#ColumnInfo(name = "bookmarked")
val bookmarked: Boolean
)
and also this is my model (if you need this)
data class TranslatedWord(
val from_word: String,
val from_word2: String?,
val from_language: String,
val to_word: String,
val to_word2: String?,
val to_language: String,
val time: String,
val date: String,
val country: String,
val city: String,
val dayOfWeek: String,
val bookmarked: Boolean
)
I think you can do a dual validation on the joins, is it possible on a where to select all fields that are either null or match a condition, like:
WHERE (:t2 IS NULL OR :t2 = :t1)
My guess is you can achieve the same doing that on the join, something like:
JOIN word AS f ON (from_word_map = f.word_id OR from_word_map IS NULL)
You can perform further validations (like checking if both fields are null to indeed return a null) nesting and's.

Trouble parsing a JSON string with unknown keys using Retrofit and Gson

I have a JSON response that includes unknown keys (the numbers), which I'm finding difficult to parse using the Gson converter. The cut-down version is
{
"list": {
"14": {
"nickname": "Dave",
"fullname": "David Collins"
},
"68": {
"nickname": "Phil",
"fullname": "Phillip Jordan"
}
},
"action": "load",
"status": "LOADED"
}
The following class structure gives an error Expected BEGIN_OBJECT but was NUMBER at line 1 column 23 path $.list..
data class NameNW(
val fullname: String,
val nickname: String
)
data class NamesListNWEntity(
val action: String,
val list: Map<Int, Map<String, NameNW>>,
val status: String
)
I'm not sure why it's expecting BEGIN_OBJECT when the type is Map<Int... (or perhaps it's not seeing the '{' before the number when it's clearly there), so I'm stuck here. How do I get it to parse the string? Even better, how do I get it to record the number for NameNW? If it's not possible, I can adjust the server output, but that means updating a lot of code on the web client as well, which I'd rather avoid.
My Retrofit code is
interface Network {
#FormUrlEncoded
#POST("serverfile")
suspend fun getNames(
#Field("action") action: String,
): NamesListNWEntity
}
class RetrofitWithCookie #Inject constructor(
context: Context,
gson: Gson
) {
private val mContext = context
private val gson = gson
fun createRetrofit(): Retrofit {
val client: OkHttpClient
val builder = OkHttpClient.Builder()
builder.addInterceptor(AddCookiesInterceptor(mContext))
builder.addInterceptor(ReceivedCookiesInterceptor(mContext))
client = builder.build()
return Retrofit.Builder()
.baseUrl("http://192.168.0.19/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
}
And I'm calling it using
val namesListNWEntity = Network.getNames("load")
Try with the following code.
data class NameNW(
val fullname: String,
val nickname: String
)
data class NamesListNWEntity(
val action: String,
val list: Map<String, NameNW>,
val status: String
)
// You can also test with static json string
private fun test() {
val apiResponse = "{\n" +
" \"list\": {\n" +
" \"14\": {\n" +
" \"nickname\": \"Dave\",\n" +
" \"fullname\": \"David Collins\"\n" +
" },\n" +
" \"68\": {\n" +
" \"nickname\": \"Phil\",\n" +
" \"fullname\": \"Phillip Jordan\"\n" +
" }\n" +
" },\n" +
" \"action\": \"load\",\n" +
" \"status\": \"LOADED\"\n" +
"}"
val namesListNWEntity: NamesListNWEntity = Gson().fromJson(apiResponse, NamesListNWEntity::class.java)
}

JSON parsing using Gson returns an error for a boolean value

I have the following json generated from a WordPress Post:
{
"events":[
{
"id":4651,
"title":"Test title",
"description":"testttt",
"image": {
url: "https://myhost.tv/wp-content/uploads/2019/07/event2.jpg",
id: "4652"}
}
]
}
My model for json is as follows:
data class EventsFeed(val events: Array<Events>)
data class Events (
val id : Int,
val title : String,
val description : String,
val image : Image
)
data class Image (
val url : String,
val id : Int,
)
I do the parsing with Json and everything works fine, but when I do a POST in wordpress and I don't put an image, the value of the Image key puts me as afalse, like this:
{
"events":[
{
"id":4651,
"title":"Stand Up Comedy",
"description":"testttt",
"image":false
}
]
}
And just because image has the valuefalse, the parsing returns an error: Expected BEGIN_OBJECT but was BOOLEAN at line 1 column 5781 path $ .events [1] .image
What can I do so that when the post has no image to parse correctly ignoring the value false or in any case if it isfalse please keep it a default image (https://myhost.com/image_defaul.jpg)
The json is generated by the plugin for Wordpress: The Events Calendar: Demo json here
My function (usen Volley and Gson) for parsing is as follows (the data array is sent to an adapter for display in a recyclerview)
fun jsonObjectRequest() {
Log.i(LOG_TAG, "jsonObjectRequest")
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(activity)
val url = "https://myhost.tv/wp-json/tribe/events/v1/events"
// Request a JSONObject response from the provided URL.
val jsonObjectRequest = JsonObjectRequest( url, null,
Response.Listener { response ->
Log.i(LOG_TAG, "Response is: $response")
val gson = Gson()
val homeEvents = gson.fromJson(response.toString(), EventsFeed::class.java)
activity?.runOnUiThread {
recyclerEvents.adapter = AdaptadorEventos(homeEvents)
}
},
Response.ErrorListener { error ->
error.printStackTrace()
Log.e(LOG_TAG, "That didn't work!")
}
)
// Add the request to the RequestQueue.
queue.add(jsonObjectRequest)
}
The reason why your post method is not getting the Image Class object is because of the json is not a valid json, You may validate it on https://jsonlint.com/. The reason is: "url" and "id" keys are not surrounded by "". Take a look at the below solution, it works perfectly:
package com.example.myapplication
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import com.google.gson.Gson
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getEvemtsObject()
}
private val jsonString = " {\n" +
" \t\"events\": [{\n" +
" \t\t\"id\": 4651,\n" +
" \t\t\"title\": \"Test title\",\n" +
" \t\t\"description\": \"testttt\",\n" +
" \t\t\"image\": {\n" +
" \t\t\t\"url\": \"https://myhost.tv/wp-content/uploads/2019/07/event2.jpg\",\n" +
" \t\t\t\"id\": \"4652\"\n" +
" \t\t}\n" +
" \t}]\n" +
" }"
private fun getEvemtsObject() {
val gson = Gson()
System.out.println("from_gson ---> " + gson.fromJson<EventsFeed>(jsonString,EventsFeed::class.java))
}
}
You can use custom Gson deserializer like this:
class EventsDeserializer : JsonDeserializer<Events> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Events {
val jsonObject = json.asJsonObject
return Events(
id = jsonObject.get("id").asInt,
title = jsonObject.get("title").asString,
description = jsonObject.get("description").asString,
image = parseImage(context, jsonObject.get("image"))
)
}
private fun parseImage(context: JsonDeserializationContext, json: JsonElement): Image =
try {
context.deserialize(json, Image::class.java)
} catch (_: Throwable) {
Image("https://myhost.com/image_defaul.jpg", 0)
}
}
and here is the test of your json:
fun main() {
val gson = GsonBuilder()
.registerTypeAdapter(Events::class.java, EventsDeserializer())
.create()
val json = """
{
"events": [
{
"id": 4651,
"title": "Stand Up Comedy",
"description": "testttt",
"image": false
}
]
}
""".trimIndent()
val events = gson.fromJson(json, EventsFeed::class.java)
}
the image parameter in constructor should be nullable.
rewrite your class like this
data class Events (
val id : Int,
val title : String,
val description : String,
var image : Image? = null)
and change your API to send null instead of false.
This might also be an option (but fixing the API is the better option):
val image: Any

Failed to parse the row anko kotlin

i dont know why this code can't parse the row. please help.
private var dataCards :MutableList<DataCard> = mutableListOf()
btn_read.setOnClickListener {
showAllCardData()
var data = dataCards
tvResult.text = ""
for (i in 0..(data.size - 1)) {
tvResult.append(data.get(i).id.toString() + " " +
data.get(i).title + " " +
data.get(i).instruction + " " +
data.get(i).level + " " +
data.get(i).rewardPoint + " " +
data.get(i).rewardExp + " " +
data.get(i).inputdate + "\n")
}
}
private fun showAllCardData(){
missionDB.use {
val result = select(DataCard.TABLE_USERCARD)
val cards = result.parseList(classParser<DataCard>())
dataCards.addAll(cards)
}
}
Any reference or suggestion ?, the error is "failed to parse the row" please help.
row you get from showAllCardData function not match with DataCard,
example row you get from database (field1,field2)
but your Datacard constructor parameter (field1,field2,field3,field4)

Categories

Resources