Android - Populate values into already created POJO model object using retrofit - android

I have a POJO model object "apiResponse" that has values from a previous API call.
{
"status":200,
"userModel":{
...SOME VARIABLES...
},
"otherContentRelatedToUserModel":{
..SOME RELATED CONTENT..
}
}
This apiResponse has an "UserModel" as an inner object.
What i want to do is pass this "apiResponse" object to another api call whose response is "UserModel", and have it update only the UserModel object in the APIResponse POJO object.
The objective is to keep a single source of related content, which could change based on the interaction in the application, but might not update the rest of the related content.
Or is it possible to atleast update an already created pojo model as a whole, updating the variable values in the model.?
Reason for this ::
The API's content does not change for a set amount of time in the server, mainly to avoid over traffic to the server. So some amount of logic has to be implemented on the application side. Currently using a DB is not really a viable option.
Basically update only a portion of the already created POJO class object with another api call.
Is this possible in android(kotlin) using retrofit? Or is there any other way this could be achievable?

I think it is not possible to populate the additional fields in the existing UserModel object by using Retrofit, but you can do some magic with GSON:
data class UserModel(
val userId: Int? = null,
val userName: String? = null)
class OtherContentRelatedToUserModel
data class ApiResponsePojo(
val status: Int? = null,
val userModel: UserModel? = null,
val otherContentRelatedToUserModel: OtherContentRelatedToUserModel? = null)
class UserModelInstanceCreator(var userModelToUpdate: UserModel? = null)
: InstanceCreator<UserModel> {
override fun createInstance(type: Type?): UserModel {
return userModelToUpdate ?: UserModel()
}
}
val apiResponseJson =
"""
{
"status":200,
"userModel":{
"userId": 1
},
"otherContentRelatedToUserModel":{
}
}
"""
val userModelResponseJson =
"""
{
"userName": "john wick"
}
"""
val userModelInstanceCreator = UserModelInstanceCreator()
val gson = GsonBuilder()
.registerTypeAdapter(UserModel::class.java, userModelInstanceCreator)
.create()
val apiResponse: ApiResponsePojo = gson.fromJson(apiResponseJson, ApiResponsePojo::class.java)
userModelInstanceCreator.userModelToUpdate = apiResponse.userModel
gson.fromJson(userModelResponseJson, UserModel::class.java)
...
// apiResponse.toString() result
// ApiResponsePojo(status=200, userModel=UserModel(userId=1, userName=john wick)...

Related

Insert list of class's object to Room by parsing API data using MVVM in Kotlin

I built an application in Kotlin using MVVM. I fetched the API response from the server successfully. Now I want to insert API's parsing data into RoomDB.
API response included both JSON Object & Array. I want to insert specific data from API to DB. Please help me with how I can make an entity class and set parsing data to the class data members with/without loops and insert it into RoomDB by making a single list of the whole class.
Please provide tutorial links or any kind of material links instead of the Android Developers Guide. Thanks a lot!
In API Response we have many data but actually, we don't need that all that so basically we to create one data class that is only constant the specific that actually, we need. and that all operation is performed in a repository and we manage it.
entity class that only contains essential data
#Entity(tableName = "wallpaper")
data class WallpaperDataClass (
#PrimaryKey(autoGenerate = true)
val note_id:Int=0,
val photoId: Int,
val photos_url: String,
val photographer_name: String,
val photographer_url: String,
val src:String
)
Fill the data in model
if (NetworkUtils.isOnline(applicationContext)) {
/**
* Online
* if Your net is online then call api
*/
try {
val result: Response<PhotoModel> =
wallpaperInterface.getWallpaper(authorization, page, per_page)
if (result.body() != null) {
val photos = mutableListOf<WallpaperDataClass>()
result.body()!!.photos.forEach {
// in blows line we set data in modal
val wallpaperDataClass = WallpaperDataClass(
photoId = it.id,
photos_url = it.url,
photographer_name = it.photographer,
photographer_url = it.photographerUrl,
src = it.src.portrait
)
photos.add(wallpaperDataClass)
if (!offlineDatabase.getDao().exists(it.id)){
offlineDatabase.getDao().insertWallpaper(wallpaperDataClass)
}
mutableLiveData.postValue(ErrorHandling.Success(photos))
}
} else {
Log.d("WallpaperResponse", "getWallpaper: ${result.message()}")
}
} catch (e: Exception) {
mutableLiveData.postValue(ErrorHandling.Faild(e.localizedMessage!!.toString()))
}
} else {
/**
* Offline
*if Your net is offline then fetch from db
*/
try {
val wallpaper = offlineDatabase.getDao().getOfflineWallpaper()
mutableLiveData.postValue(ErrorHandling.Success(wallpaper))
} catch (e: Exception) {
mutableLiveData.postValue(ErrorHandling.Faild(e.localizedMessage!!.toString()))
}
}
}
}
Video Tutorial

Reference an object in a class by using a string?

I want to reference an object within this class I have below:
class HerbData {
object Dill {
const val herbName: String = "This is Dill!"
const val scientificName: String = "Anethum Graveolens"
val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
}
object Peppermint {
val herbName: String = "This is Peppermint!"
}
}
Is there anyway that I can reference the object by using a string in Kotlin? Here is somewhat what I mean:
HerbData."Dill".herbname
I can't find anything on this topic for Kotlin.
Another way you could do this is with an enum class. The advantage over a map is that you have a data structure you can reference directly in code, so you could use HerbData.Dill as well as HerbData["Dill"]. And that will enable you to take advantage of compile-time checking and lint warnings, refactoring, exhaustive pattern matching, code completion etc, because the data is defined in your code
enum class HerbData(
val herbName: String,
val scientificName: String? = null,
val dullThumbnail: Int? = null
) {
Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
Peppermint("This is Peppermint!");
companion object {
operator fun get(name: String): HerbData? =
try { valueOf(name) } catch(e: IllegalArgumentException) { null }
}
}
fun main() {
// no guarantee these lookups exist, need to null-check them
HerbData["Peppermint"]?.herbName.run(::println)
// case-sensitive so this fails
HerbData["peppermint"]?.herbName.run(::println)
// this name is defined in the type system though! No checking required
HerbData.Peppermint.herbName.run(::println)
}
>> This is Peppermint!
null
This is Peppermint!
Enum classes have that valueOf(String) method that lets you look up a constant by name, but it throws an exception if nothing matches. I added it as a get operator function on the class, so you can use the typical getter access like a map (e.g. HerbData["Dill"]). As an alternative, you could do something a bit neater:
companion object {
// storing all the enum constants for lookups
private val values = values()
operator fun get(name: String): HerbData? =
values.find() { it.name.equals(name, ignoreCase = true) }
}
You could tweak the efficiency on this (I'm just storing the result of values() since that call creates a new array each time) but it's pretty simple - you're just storing all the enum entries and creating a lookup based on the name. That lets you be a little smarter if you need to, like making the lookup case-insensitive (which may or may not be a good thing, depending on why you're doing this)
The advantage here is that you're generating the lookup automatically - if you ever refactor the name of an enum constant, the string label will always match it (which you can get from the enum constant itself using its name property). Any "Dill" strings in your code will stay as "Dill" of course - that's the limitation of using hardcoded string lookups
The question really is, why do you want to do this? If it's pure data where no items need to be explicitly referenced in code, and it's all looked up at runtime, you should probably use a data class and a map, or something along those lines. If you do need to reference them as objects within the code at compile time (and trying to use HerbData."Dill".herbName implies you do) then an enum is a fairly easy way to let you do both
Declare a Data Class
data class HerbData (
val scientificName: String,
val dullThumbnail: Int
)
Initialize a muteable map and put data in it
val herbData = mutableMapOf<String, HerbData>()
herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
You can now just
herbData["Dill"]?.scientificName
class HerbData {
interface Herb {
val herbName: String
val scientificName: String
}
object Dill : Herb {
override val herbName: String = "This is Dill!"
override val scientificName: String = "Anethum Graveolens"
}
object Peppermint: Herb {
override val herbName: String = "This is Peppermint!"
override val scientificName: String = "Mentha piperita"
}
companion object {
operator fun get(name: String): Herb? {
return HerbData::class
.nestedClasses
.find { it.simpleName == name }
?.objectInstance as? Herb
}
}
}
println(HerbData["Dill"]?.herbName) // Prints: This is Dill!
println(HerbData["Peppermint"]?.scientificName) // Prints: Mentha piperita
println(HerbData["Pepper"]?.herbName) // Prints: null

Using a coroutine inside a Transform.map function

Well here is a tricky one.
I have a query from my Room DB. The result is a Relational POJO
class MessageWithMsgQueueAccount {
#Embedded
var message: MessageDto? = null
#Relation(parentColumn = "clientMessageId", entityColumn = "clientMessageId", entity = MessageQueueDto::class)
var messageQueueList: List<MsgQueueAccount> = ArrayList()
}
So when i get that i apply a Transformation to this object so i can create another one that has only the information that i want.
fun toContactMessageChatItem(item: MessageWithMsgQueueAccount?, accountDto: AccountDto): MessageChatItem {
item?.message?.let {
// Procedure for creating the fields that i want
val isQuoted = it.quotemsgid > 0L
if (isQuoted) {
// Fetch quoted message body
}
return MessageChatItem(.....)
} ?: run {
return MessageChatItem(..........)
}
}
Since this is a chat, one field that i want to setup is a quoted message body. What i have as "input" is the messageId of the message that is being quoted. So that means that i have to make a query to my Room DB again inside the transform function.
The way i did it is this
val isQuoted = it.quotemsgid > 0L
var quotedBody = ""
if (isQuoted) {
// Fetch quoted message body
viewModelScope.launch(Dispatchers.IO) {
val quotedMessage = messagesRepository.getMessageByMessageId(it.quotemsgid)
withContext(Dispatchers.Main) {
quotedBody = quotedMessage.body
}
}
}
There no specific question but is there any better way to do something like this, meaning querying the DB inside a Transformation function. Is there a way that this would create a synchronize problem to my elements or something?

onRetrofitFailure() Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token

I am given a Json data where an image and some other data is stored. I am trying to fetch that image in an imageview using Retrofit. I created a DTO of the required things as detailed in the code. I am getting and error, on retrofit failure. How can I solve this?
Created Retrofit Instance
Created DTO of JSON data and properties
API service created also
https://s3.ap-south-1.amazonaws.com/zestlife/promotional_banner.json
Link where JSON data is stored.
#Parcelize
#JsonIgnoreProperties(ignoreUnknown = true)
open class MerchantPromotionDTO(
#JsonProperty("image") var image: ImageUrlsDTO? = null,
#JsonProperty("cta") var cta: CTADTO? = null,
#JsonProperty("probability") var probability: Int? = 0,
#JsonProperty("isDismissible") var isDismissible: Boolean? = true,
#JsonProperty("showImmediate") var showImmediate: Boolean? = false
) : BaseResponseDTO()
#Parcelize
#JsonIgnoreProperties(ignoreUnknown = true)
class MerchantpromotionBDTO(
#JsonProperty("promotions") var promotions: ArrayList<MerchantPromotionDTO>?=null
) : BaseResponseDTO()
#GET("https://demo6861386.mockable.io/banner/test")
fun getPromotionalBanner(): Call<ArrayList<MerchantpromotionBDTO>>
fun getPromotionalDetails(): LiveData<ResponseDTO<ArrayList<MerchantpromotionBDTO>>>{
val pBannerDetails=MutableLiveData<ResponseDTO<ArrayList<MerchantpromotionBDTO>>>()
ApiComponent.enqueue({
getPromotionalBanner(
)
},object :OnRequestComplete<ArrayList<MerchantpromotionBDTO>>{
override fun onComplete(responseDTO: ResponseDTO<ArrayList<MerchantpromotionBDTO>>) {
pBannerDetails.value=responseDTO
}
}
)
EDIT:
override fun onStart() {
super.onStart()
populateData()
}
#Synchronized
private fun populateData() {
MerchantpromotionBDTO?.let {
val promImageUrl = it.image?.getImageUrl(CommonUtils.getDisplayDensityLevel(context))
picasso.load(if (promImageUrl.isNullOrEmpty()) null else promImageUrl)
.placeholder(R.drawable.ic_placeholder_minimal)
.into(ivMerchantPromotionBanner)
}
}
I want the response into my logcat and to fetch an image in Imageview
The issue is that the JSON you're getting from the backend is:
{
"promotions": [
...
]
}
Thas is a JSON object, not an array, but you defined getPromotionalBanner method as returning a List<MerchantpromotionBDTO>, so the JSON library cannot perform deserialisation as it expects an array (i.e. something starting with [) but it finds a START_OBJECT token (i.e., {).
You can solve the issue by changing the signature of that method to:
#GET("https://demo6861386.mockable.io/banner/test")
fun getPromotionalBanner(): Call<MerchantpromotionBDTO>

Android App crashes as Json element is empty String ("") and not Object

I am working on an android project and using RxAndroid, Retrofit to make API call and retrieve json. The json looks something like following :
{
"result": [
{
"parent": "jhasj",
"u_deviation": "skasks",
"caused_by": "ksks",
"u_owner_mi": {
"link": "https://gddhdd.service-now.com/api/now/v1/table/sys_user/ghtytu",
"value": "ghtytu"
},
"impact": "",
}
]
}
I am using gson to parse the Json. The problem is "u_owner_mi" sometimes reruns empty string "" when there is no value assigned to it. I don't have access to change the return type to null. This is making my app crash as I am expecting an object here.
I get the following error :
Expected BEGIN_OBJECT but was STRING
If you can't modify the server, try replacing the offending line in the server response before passing it to the Gson parser. Something like:
String safeResponse = serverResponse.replace("\"u_owner_mi\": \"\"", "\"u_owner_mi\": null");
Your app (client) code is expecting an object according to a contract specified in the class that you pass to GSON. Your app behaves as it should and crashes loudly. You should consider having your server return "u_owner_mi" : null instead of an empty string, assuming you have control over that. The u_owner_mi field on the client side would have to be a nullable type.
If you don't have the ability to fix the api, you could also write a custom deserializer.
Suppose your result class and sub-object are:
data class Result(
val parent: String,
val owner: Any?
)
data class Owner(
val link: String,
val value: String
)
The deserializer could be:
class ResultDeserializer : JsonDeserializer<Result> {
override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext?): Result {
val jsonObject = json.asJsonObject
val ownerProperty = jsonObject.get("owner")
return Result(
parent = jsonObject.get("parent").asString,
owner = if (ownerProperty.isJsonObject) context?.deserialize<Owner>(ownerProperty.asJsonObject, Owner::class.java)
else ownerProperty.asString
)
}
}
Finally, to add the deserializer:
#Test
fun deserialization() {
val gson = GsonBuilder().registerTypeAdapter(Result::class.java, ResultDeserializer()).create()
val result1 = gson.fromJson<Result>(jsonWithObject, Result::class.java)
val result2 = gson.fromJson<Result>(jsonWithEmpty, Result::class.java)
}

Categories

Resources