com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException - json

I am facing such an error in my application. I guess the problem is due to having char in note_title and note_desc. I couldn't find the solution. Is there anyone who can help?
navgraph
error
NoteDetailScreen
notes entity
? and other char cause error
I tried change note_title and note_desc types but didnt work.

I solved this way;
Let’s say you have a class like this:
#Parcalize
#Entity(tableName = "NOTES")
data class Notes(
#PrimaryKey(autoGenerate = true)
#ColumnInfo("note_id") #NotNull var note_id:Int,
#ColumnInfo("note_title") #NotNull var note_title: String,
#ColumnInfo("note_desc") #NotNull var note_desc: String,
#ColumnInfo("note_date") #Nullable var note_date: String?
): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString().toString(),
parcel.readString().toString(),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(note_id)
parcel.writeString(note_title)
parcel.writeString(note_desc)
parcel.writeString(note_date)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Notes> {
override fun createFromParcel(parcel: Parcel): Notes {
return Notes(parcel)
}
override fun newArray(size: Int): Array<Notes?> {
return arrayOfNulls(size)
}
}
}
annotation class Parcalize
You can define the NavType like this:
class NavTypo : NavType<Notes>(isNullableAllowed = false) {
override fun get(bundle: Bundle, key: String): Notes? {
return bundle.getParcelable(key)
}
override fun parseValue(value: String): Notes {
return Gson().fromJson(value, Notes::class.java)
}
override fun put(bundle: Bundle, key: String, value: Notes) {
bundle.putParcelable(key, value)
}
}
And use it:
composable(
"note_details_page/{note_id}",
arguments = listOf(
navArgument("note_id"){
type = NavTypo()
}
)
){
val note = it.arguments?.getParcelable<Notes>("note_id")
if (note != null) {
NoteDetailScreen(note, navController)
}
}
Card(
backgroundColor = choosedColor,
modifier = Modifier
.padding(3.dp)
.sizeIn(maxHeight = 250.dp)
.combinedClickable(onClick={
val note = allNotes.value!![it]
val noteJson = Uri.encode(Gson().toJson(note))
navController.navigate("note_details_page/${noteJson}")
}

Related

I can access my fun inside the ViewHolder from the adapter

I try to call fun bind declared in the inner class LaunchesViewHolder from onBindViewHolder() but I got error "Unresolved resource bind"
I was trying with an other variable x, just to see, same problem
class LaunchesAdapter(private val dataSet: List<LaunchItem>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class LaunchesViewHolder( val binding: LaunchesItemLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
val x = 0
public fun bind(currentLaunch: LaunchItem) {
//do something
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return LaunchesViewHolder(
LaunchesItemLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.bind(dataSet[position]) => error unresolved resource bind
holder.x =1 => error unresolved resource x
}
override fun getItemCount(): Int {
return dataSet.size
}
}````
In your onBindViewHolder you should use your specific ViewHolder, that is LaunchesViewHolder and not the RecyclerView.ViewHolder. Please see code below.
override fun onBindViewHolder(holder: LaunchesViewHolder, position: Int) {
holder.bind(dataSet[position])
}
Edited:
You need to specify the class you override too
class LaunchesAdapter(private val dataSet: List<LaunchItem>) :
RecyclerView.Adapter<LaunchesAdapter.LaunchesViewHolder>() {
}
it works with
(holder as LaunchesViewHolder).bind(dataSet[position])
instead of holder.bind(dataSet[position])
see more details https://www.section.io/engineering-education/implementing-multiple-viewholders-in-android-using-kotlin/

Make JsonDeserializer global effect

When deserialize json to Map<out Any, Any>, gson will use Double to fill the Map, even the field is int number, so I use a MapDeserializerDoubleAsIntFix to covert number to int if it is possible.
{
"person":{
"name":"jack",
"age":24,
"height":174.5
}
}
class MapDeserializerDoubleAsIntFix: JsonDeserializer<Map<out Any, Any>> {
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): Map<out Any, Any>? {
return deserialize(json) as Map<out Any, Any>
}
private fun deserialize(jsonElement: JsonElement): Any? {
when {
jsonElement.isJsonArray -> {
val list: MutableList<Any?> = ArrayList()
val arr = jsonElement.asJsonArray
for (anArr in arr) {
list.add(deserialize(anArr))
}
return list
}
jsonElement.isJsonObject -> {
val map: MutableMap<String, Any?> = LinkedTreeMap()
val obj = jsonElement.asJsonObject
val entitySet = obj.entrySet()
for ((key, value) in entitySet) {
map[key] = deserialize(value)
}
return map
}
jsonElement.isJsonPrimitive -> {
val prim = jsonElement.asJsonPrimitive
when {
prim.isBoolean -> {
return prim.asBoolean
}
prim.isString -> {
return prim.asString
}
prim.isNumber -> {
// Here is what i do
// use int or long if it is possible
val numStr = prim.asString
return if (numStr.contains(".")) {
prim.asDouble
} else {
val num = prim.asNumber
val numLong = num.toLong()
return if (numLong < Int.MAX_VALUE && numLong > Int.MIN_VALUE) {
numLong.toInt()
} else {
numLong
}
}
}
}
}
}
return null
}
}
object MapTypeToken: TypeToken<Map<out Any, Any>>()
private val GSON = GsonBuilder()
.registerTypeAdapter(MapTypeToken.type, MapDeserializerDoubleAsIntFix())
.create()
And when I use GSON deserialize json as map, it works.
val map: Map<out Any, Any> = GSON.fromJson(json, MapTypeToken.type)
But when the map is in a data class as a field, the MapDeserializerDoubleAsIntFix not work.
data class Test(
val person: Map<out Any, Any>
)
val test = GSON.fromJson(json, Test::class.java)
So is there any way to deserialize map or filed map ?
Now I use this to solve.
class TestJsonDeserializer: JsonDeserializer<Test> {
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext,
): Test {
val jsonObject = json.asJsonObject
val personElement = jsonObject.get("person")
val person = context.deserialize<Map<out Any, Any>>(personElement, MapTypeToken.type)
return Test(person)
}
}
private val GSON = GsonBuilder()
.registerTypeAdapter(MapTypeToken.type, MapDeserializerDoubleAsIntFix())
.registerTypeAdapter(Test::class.java, TestJsonDeserializer())
.create()
But if another data class has map, I have to write another JsonDeserializer and register it.
Hope there is a better way to make the MapDeserializerDoubleAsIntFix work as global.

Asynchronous call in Kotlin not working with RecycleView

I'm struggling with asynchronous calls - the app is just crashing. I want to load a JSON-file (containing 100 JSON-objects) from an URL and then send it to RecyclerView.
Here is the MainActivity-class:
class MainActivity : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
lateinit var linearLayoutManager: LinearLayoutManager
private val url = [//some address here]
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = linearLayoutManager
AsyncTaskHandler().execute(url)
}
inner class AsyncTaskHandler : AsyncTask<String, String, String>() {
override fun onPreExecute() {
super.onPreExecute()
}
override fun doInBackground(vararg url: String?): String {
val text: String
val connection = URL(url[0]).openConnection() as HttpURLConnection
try {
connection.connect()
text = connection.inputStream.use { it.reader().use {reader -> reader.readText()} }
} finally {
connection.disconnect()
}
return text
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
handleJson(result)
}
}
private fun handleJson(jsonString: String?) {
val jsonArray = JSONArray(jsonString)
var list = mutableListOf<DataSet>()
var i = 0
while (i < jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
list.add(DataSet(
jsonObject.getString("title"),
jsonObject.getString("type")
))
i++
}
val adapter = Adapter(list)
recyclerView.adapter = adapter
}
}
...and ListAdapter-class:
class Adapter (private var targetData: MutableList<DataSet>): RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.element, parent, false)
return ViewHolder(v);
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = targetData[position]
holder.title?.text = item.title
holder.type?.text = item.type
}
override fun getItemCount(): Int {
return targetData.size
}
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var title = itemView.findViewById<TextView>(R.id.itemTitle)
var type = itemView.findViewById<TextView>(R.id.itemType)
}
What might be the problem here? Is there any better option to perform this?

Unable to access Seq elements and return entire Seq as Json

So I am extremely new to Scala and seem to be doing something horribly wrong for a simple logic.
I need to query database to get all the WebsiteTemplates.
I am then trying to access the first element of the sequence (simply for practice) but keep getting the ClassCastException - [Ljava.lang.Object; cannot be cast to models.WebsiteTemplate. Next, I would want to return the entire Seq obtained as Json. Tried Json.arr() but won't work. Searched but none matches the exact use case strangely. Here is relevant the code -
WebsiteController
#Singleton
class WebsiteTemplateController #Inject()(websiteTemplateDAO: WebsiteTemplateDAO, db: DB) extends Controller{
def index = Action.async {
implicit request => db.withTransaction() {
implicit em => {
try {
val templates: Seq[WebsiteTemplate] = websiteTemplateDAO.findAll()
val template = templates(0)
Logger.info("The size is " + template.name)
Future(Ok(Json.obj(
"message" -> "Success"
)))
} catch {
case e: Exception =>
e.printStackTrace()
Future(InternalServerError(s"Lag gaye"))
}
}
}
}
}
WebsiteDAOImpl
#ImplementedBy(classOf[WebsiteTemplateDAOImpl])
trait WebsiteTemplateDAO extends DAO[WebsiteTemplate]{
def findAll()(implicit em: EntityManager): Seq[WebsiteTemplate]
}
#Singleton
class WebsiteTemplateDAOImpl #Inject()(db: DB) extends DAOImpl(classOf[WebsiteTemplate]) with WebsiteTemplateDAO{
override def findAll()(implicit em: EntityManager): Seq[WebsiteTemplate] = {
val query = em.createQuery(s"SELECT idint, name FROM WebsiteTemplate")
db.executeQuery(query)
}
}
DAOImpl
trait DAO[T] {
def findById(id:String)(implicit em:EntityManager) : Option[T]
def create : T
}
class DAOImpl[T](cls:Class[T]) extends DAO[T] {
def findById(id: String)(implicit em: EntityManager): Option[T] = {
val query = em.createQuery(s"Select c from ${cls.getName} as c where c.id=:id")
query.setParameter("id",id)
val list = query.getResultList
if (list.nonEmpty) {
Option(list(0).asInstanceOf[T])
} else {
None
}
}
override def create: T = {
cls.newInstance()
}
}
WebsiteTemplate
#Entity
#Table(name = "templates")
#EntityListeners(Array(classOf[SetForeignKeyOnSave]))
class WebsiteTemplate{
#Id
#Column(name = "id")
#BeanProperty
var intid: java.lang.Integer = _
/*#Transient
override var id: String = _*/
#BeanProperty
#Column(name = "name")
var name: String = _
}
A bit more explanation of my problem. On getting the type of templates(0) using getClass() method, it shows as [Ljava.lang.Object whereas I expect it to be of type WebsiteTemplate.

retrofit + gson deserializer: return inside array

I have api that return json:
{"countries":[{"id":1,"name":"Australia"},{"id":2,"name":"Austria"}, ... ]}
I write model class (Kotlin lang)
data class Country(val id: Int, val name: String)
And I want do request using retorift that returning List < Models.Country >, from "countries" field in json
I write next:
interface DictService {
#GET("/json/countries")
public fun countries(): Observable<List<Models.Country>>
companion object {
fun create() : DictService {
val gsonBuilder = GsonBuilder()
val listType = object : TypeToken<List<Models.Country>>(){}.type
gsonBuilder.registerTypeAdapter(listType, CountriesDeserializer)
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
val service = Retrofit.Builder()
.baseUrl("...")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
.build()
return service.create(DictService::class.java)
}
}
object CountriesDeserializer : JsonDeserializer<List<Models.Country>> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): List<Models.Country>? {
val res = ArrayList<Models.Country>()
if(json!=null) {
val countries = json.asJsonObject.get("countries")
if (countries.isJsonArray()) {
for (elem: JsonElement in countries.asJsonArray) {
res.add(Gson().fromJson(elem, Models.Country::class.java))
}
}
}
return null;
}
}
}
But I get error:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
CountriesDeserializer code dont execute even!
What they want from me?
Maybe I need write my own TypeAdapterFactory?
I dont want use model class like
class Countries {
public List<Country> countries;
}
If your intention is to simplify the interface and hide the intermediate wrapper object I guess the simplest thing to do is to add an extension method to the DictService like so:
interface DictService {
#GET("/json/countries")
fun _countries(): Observable<Countries>
}
fun DictService.countries() = _countries().map { it.countries }
data class Countries(val countries: List<Country> = listOf())
Which can then be used as follows:
val countries:Observable<List<Country>> = dictService.countries()
I found the way:
object CountriesTypeFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson?, type: TypeToken<T>?): TypeAdapter<T>? {
val delegate = gson?.getDelegateAdapter(this, type)
val elementAdapter = gson?.getAdapter(JsonElement::class.java)
return object : TypeAdapter<T>() {
#Throws(IOException::class)
override fun write(outjs: JsonWriter, value: T) {
delegate?.write(outjs, value)
}
#Throws(IOException::class)
override fun read(injs: JsonReader): T {
var jsonElement = elementAdapter!!.read(injs)
if (jsonElement.isJsonObject) {
val jsonObject = jsonElement.asJsonObject
if (jsonObject.has("countries") && jsonObject.get("countries").isJsonArray) {
jsonElement = jsonObject.get("countries")
}
}
return delegate!!.fromJsonTree(jsonElement)
}
}.nullSafe()
}
}
But it is very complex decision, I think, for such problem.
Are there another one simpler way?
Another one:
I found bug in my initial code from start meassage!!!
It works fine if replace List by ArrayList!
I would use Jackson for this task.
Try this https://github.com/FasterXML/jackson-module-kotlin
val mapper = jacksonObjectMapper()
data class Country(val id: Int, val name: String)
// USAGE:
val country = mapper.readValue<Country>(jsonString)