I have an object that I wrote to a file and now I want to take that string I get from the file to turn it back into an object for me to use.
Object class:
#Serializable
class DrinkItem {
#SerializedName("strAlcoholic")
val alcoholic: String? = null
#SerializedName("strIngredient1")
val ingredient1: String? = null
#SerializedName("strIngredient10")
val ingredient10: String? = null
#SerializedName("strIngredient11")
val ingredient11: String? = null
#SerializedName("strIngredient12")
val ingredient12: String? = null
#SerializedName("strIngredient13")
val ingredient13: String? = null
#SerializedName("strIngredient14")
val ingredient14: String? = null
#SerializedName("strIngredient15")
val ingredient15: String? = null
#SerializedName("strIngredient2")
val ingredient2: String? = null
#SerializedName("strIngredient3")
val ingredient3: String? = null
#SerializedName("strIngredient4")
val ingredient4: String? = null
#SerializedName("strIngredient5")
val ingredient5: String? = null
#SerializedName("strIngredient6")
val ingredient6: String? = null
#SerializedName("strIngredient7")
val ingredient7: String? = null
#SerializedName("strIngredient8")
val ingredient8: String? = null
#SerializedName("strIngredient9")
val ingredient9: String? = null
#SerializedName("strInstructions")
val instructions: String? = null
#SerializedName("strMeasure1")
val measurement1: String? = null
#SerializedName("strMeasure10")
val measurement10: String? = null
#SerializedName("strMeasure11")
val measurement11: String? = null
#SerializedName("strMeasure12")
val measurement12: String? = null
#SerializedName("strMeasure13")
val measurement13: String? = null
#SerializedName("strMeasure14")
val measurement14: String? = null
#SerializedName("strMeasure15")
val measurement15: String? = null
#SerializedName("strMeasure2")
val measurement2: String? = null
#SerializedName("strMeasure3")
val measurement3: String? = null
#SerializedName("strMeasure4")
val measurement4: String? = null
#SerializedName("strMeasure5")
val measurement5: String? = null
#SerializedName("strMeasure6")
val measurement6: String? = null
#SerializedName("strMeasure7")
val measurement7: String? = null
#SerializedName("strMeasure8")
val measurement8: String? = null
#SerializedName("strMeasure9")
val measurement9: String? = null
#SerializedName("strDrink")
val name: String? = null
#SerializedName("strDrinkThumb")
val thumbnail: String? = null
}
I use this function to write the object to a file:
private fun writeToFile(fileName: String, byteArray: ByteArray){
val lineSeparator: String = System.getProperty("line.separator") as String
// File
val path = context!!.filesDir
val directory = File(path, "LET")
directory.mkdirs()
val file = File(directory, fileName)
//append drink to file
FileOutputStream(file, true).use {
it.write(byteArray)
it.write(lineSeparator.toByteArray())
}
}
After the function is done with the object it is turned into this string:
{"alcoholic":"Alcoholic","ingredient1":"Apricot brandy","ingredient2":"Triple sec","ingredient3":"Lime","ingredient4":"Lime","instructions":"Shake all ingredients (except lime wedge) with ice and strain into a cocktail glass. Add the wedge of lime and serve.","measurement1":"1 oz ","measurement2":"1 oz ","measurement3":"Juice of 1 ","measurement4":"1 ","name":"After Dinner Cocktail","thumbnail":"https://www.thecocktaildb.com/images/media/drink/vtytxq1483387578.jpg"}
Is there a function or library that would help me turn a string of an object into said object?
Are you using library to convert your DrinkItem to the outputting JSON String? Seems that the keys in your JSON String do not match with what you have named in #SerializedName().
You can use the Gson library to handle JSON String and Object conversion.
For example, if you have the same DrinkItem class, you can convert your DrinkItem into JSON String as following:
// Define DrinkItem and set some of the attributes
val drinkItemTest = DrinkItem()
drinkItemTest.alcoholic = "Alcohol One"
drinkItemTest.ingredient1 = "Ingredient One"
// Use Gson library to convert Object to JSON String
val drinkItemTestString = Gson().toJson(drinkItemTest)
println(drinkItemTestString)
Output
{"strAlcoholic":"Alcohol One","strIngredient1":"Ingredient One"}
And to convert your JSON String back to DrinkItem, you can do something like this:
// Read the whole JSON String from your file here
val drinkItemString = "{\"strAlcoholic\":\"Alcohol One\",\"strIngredient1\":\"Ingredient One\"}"
// And make use of Gson library to convert your JSON String into DrinkItem Object
val drinkItem = Gson().fromJson(drinkItemString, DrinkItem::class.java)
Related
Following is my code
var items : MutableList<Any> = arrayListOf()
items.add(TeacherDetails(it?.photo,it?.firstName,it?.lastName,it?.level))
items.add(TeacherBio(it?.bio))
items.add(TitleAccreditations(getString(R.string.acreditations)))
items.add(SessionsTitle(it?.firstName + getString(R.string.apostrophe) + getString(
R.string.sessions)))
items.addAll(listOf(it?.classes ?: arrayListOf()))
items.add(IntroVideo(it?.introVideo))
items.addAll(it?.teachingAccreditations?.split("\n")?.map { Accreditation(
it
) }?: emptyList())
Issue is at following line it is adding entire list as object instead of individual item.
items.addAll(listOf(it?.classes ?: arrayListOf()))
Following is my model
data class Teacher(
#field:SerializedName("firstName")
val firstName: String? = null,
#field:SerializedName("lastName")
val lastName: String? = null,
#field:SerializedName("teacherId")
val teacherId: String? = null,
#field:SerializedName("introVideo")
val introVideo: String? = null,
#field:SerializedName("level")
val level: String? = null,
#field:SerializedName("teachingAccreditations")
val teachingAccreditations: String? = null,
#field:SerializedName("classes")
val classes: List<ClassesItem?>? = null,
#field:SerializedName("photo")
val photo: String? = null,
#field:SerializedName("bio")
val bio: String? = null
)
I guess because your type of list is Any, it will consider adding list instance also as an object, so instead it as listOf() just add directly
items.addAll(it?.classes?.filterNotNull()?: arrayListOf())
I have a JSON string that I need to converted to data class object in Kotlin, the problem is that there is a field (details) that can have a different structure depending of the value of another field like this
val jsonString1 = "{'name': 'Juan', 'phase': 'step1', 'details': { 'name': 'product 1' }}"
val jsonString2 = "{'name': 'Juan', 'phase': 'step2', 'details': { 'position': 10 }}"
now I have something like
data class Customer(
var name: String? = null
var phase: String? = null
var details: Details? = null
)
data class Details(
var name: String? = null
)
data class Details2(
var position: Int? = null
)
now with gson I know I can
Gson().fromJson(jsonString1, Customer::class.java)
I want to be able to automatically use the right data class depending on the value of the phase field, I know I can create an adapterFactory, but I can't figure out how, an in kotlin is worse
I was reading this post
http://anakinfoxe.com/blog/2016/02/01/gson-typeadapter-and-typeadapterfactory/
and I'm pretty sure is the way to go, but I can't quite get it
Yep, it's pretty easy to write such adapter. I've slightly changed your example:
data class Customer(
var name: String? = null,
var phase: String? = null,
var details: Details? = null
)
sealed class Details {
data class Details1(var name: String? = null) : Details()
data class Details2(var position: Int? = null) : Details()
}
class CustomerDeserializer : JsonDeserializer<Customer> {
override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext?): Customer {
val customerObject = json.asJsonObject
val detailsObject = customerObject.getAsJsonObject("details")
val details = if (detailsObject.has("name")) {
Details.Details1(detailsObject.get("name").asString)
} else {
Details.Details2(detailsObject.get("position").asInt)
}
return Customer(
name = customerObject.get("name").asString,
phase = customerObject.get("phase").asString,
details = details
)
}
}
fun main() {
val gson = GsonBuilder()
.registerTypeAdapter(Customer::class.java, CustomerDeserializer())
.create()
println(gson.fromJson(jsonString1, Customer::class.java))
println(gson.fromJson(jsonString2, Customer::class.java))
}
data class Customer(
var name: String? = null
var phase: String? = null
var details: Details? = null
)
data class Details(
var name: String? = null
var position: Int? = null
)
Define Details class in this way
Gson().fromJson(jsonString1, Customer::class.java)
return a Customer either name is null or position is null
I am working on an Android application in Kotlin which integrate Firebase.
Now I want to store my data (Kotlin data class) into Firebase Database.
Data Classes:
#Parcelize
data class Trip(
val fromAddress: String,
val toAddress: String,
val fromLocation: String,
val toLocation: String,
val orderUid: String
) : Parcelable
#Parcelize
data class Order(val trip: Trip, val date: Date, var status: OrderStatus, val userUid: String) : Parcelable {
var pickUpDate: Date? = null
var dropOffDate: Date? = null
var price: Double? = null
}
Fireabase Database write operation:
fun createNewOrder(
fromAddress: String,
toAddress: String,
fromLocation: Location,
toLocation: Location
) {
val fromGeoLocation = fromLocation.convertToGeoLocation()
val toGeoLocation = toLocation.convertToGeoLocation()
val userUid = sharedPreferences[CURRENT_USER_UID_KEY, ""]!!
val orderKey = databaseReference.child(DB_ORDERS_KEY).push().key
val tripKey = databaseReference.child(DB_TRIPS_KEY).push().key
val trip = orderKey?.let { createNewTrip(fromAddress, toAddress, it) }
val order = trip?.let { Order(it, Date(), OrderStatus.PENDING, userUid) }
if (trip != null && order != null && !userUid.isNullOrEmpty()) {
ordersGeoFire.setLocation(trip.fromGeoLocation, fromGeoLocation)
ordersGeoFire.setLocation(trip.toGeoLocation, toGeoLocation)
val allData = mutableMapOf<String, Any>()
allData["/$DB_TRIPS_KEY/$tripKey"] = trip?.convertToMap()
allData["/$DB_ORDERS_KEY/$orderKey"] = order?.convertToMap()
allData["/$DB_USERS_KEY/$userUid/$DB_ORDERS_KEY/$orderKey"] = true
databaseReference.updateChildren(allData)
}
}
I received this error:
com.google.firebase.database.DatabaseException: No properties to serialize found on class kotlin.Unit
Any suggestions?
The problem in your code is that the fileds inside your Trip class are not initialized. A recommended way in which you can create your model class would be:
class Trip(
val displayName: String = "",
val email: String = "",
val photoUrl: String = "",
val userId: String = ""
)
This is only what you need. And a way to create a new object of your Trip class, would be:
val trip = Trip(displayName, email, photoUrl, userId)
It was my mistake, because I was forget to add return type in my extensions convertToMap functions. Now they look like this:
fun Trip.convertToMap(): MutableMap<String, Any> {
val map = mutableMapOf<String, Any>()
map["fromAddress"] = fromAddress
map["toAddress"] = toAddress
map["fromGeoLocation"] = fromGeoLocation
map["toGeoLocation"] = toGeoLocation
map["orderUid"] = orderUid
return map
}
And also thanks to #Alex Mamo for his answer, it helps me in my investigation.
Now my code looks like this and works fine:
#Parcelize
data class Trip(
var fromAddress: String = "",
var toAddress: String = "",
var fromGeoLocation: String = "",
var toGeoLocation: String = "",
var orderUid: String = ""
) : Parcelable
#Parcelize
data class Order(
var trip: Trip? = null,
var date: Date? = null,
var status: OrderStatus? = null,
var userUid: String = ""
) : Parcelable {
var pickUpDate: Date? = null
var dropOffDate: Date? = null
var price: Double? = null
}
fun createNewOrder(
fromAddress: String,
toAddress: String,
fromLocation: Location,
toLocation: Location
): LiveData<Order> {
orderLiveData = MutableLiveData()
orderLiveData.value = null
val userUid = sharedPreferences[CURRENT_USER_UID_KEY, ""]!!
val orderKey = databaseReference.child(DB_ORDERS_KEY).push().key
val tripKey = databaseReference.child(DB_TRIPS_KEY).push().key
val trip = orderKey?.let { createNewTrip(fromAddress, toAddress, fromLocation, toLocation, it) }
val order = trip?.let { Order(it, Date(), OrderStatus.PENDING, userUid) }
if (trip != null && order != null && !userUid.isNullOrEmpty()) {
val allData = mutableMapOf<String, Any>()
allData["/$DB_TRIPS_KEY/$tripKey"] = trip.convertToMap()
allData["/$DB_ORDERS_KEY/$orderKey"] = order.convertToMap()
allData["/$DB_USERS_KEY/$userUid/$DB_ORDERS_KEY/$orderKey"] = true
databaseReference.updateChildren(allData) { databaseError, databaseReference ->
if (databaseError == null) orderLiveData.value = order
}
}
return orderLiveData
}
Hope this will be helpful
How to prevent gson from recieving null object from web api
when Web api return null
{ "events": null }
it will become this exception
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter data
Gson usage
val data = gson.fromJson(apiRepository
.doRequest(TheSportDBApi.getLatestMatch(league)),
MatchResponse::class.java)
My data class
data class MatchResponse (val events: List<Match>)
Match.class
data class Match(
#SerializedName("idEvent")
var eventId: String? = null,
#SerializedName("strDate")
var matchDate: String? = null,
//home
#SerializedName("strHomeTeam")
var homeTeam: String? = null,
#SerializedName("intHomeScore")
var homeScore: String? = null,
var homeBadger:String?=null,
//away
#SerializedName("strAwayTeam")
var awayTeam: String? = null,
#SerializedName("intAwayScore")
var awayScore: String? = null,
var awayBadge: String? = null
)
Is your event variable of the MatchResponse class nullable? Something like:
val event: Event?
You can make that particular object nullable using ? in kotlin
data class Article(
val title: String,
val body: String,
val events: Events? = null, //Make it nullable
val payWall: Boolean = false,
val titleImage: String? = null
)
I have a class defined like this:
data class Medicalnfo(
#Json(name = "allergies") val allergies: List<Allergy>? = null
): ProfileModel(){
#Json(name = "has_allergies") val hasAllergies: Boolean = allergies != null && allergies.isNotEmpty()
}
The problem is that hasAllergies is not included in the json serialization generated by Moshi. How can this be achieved?
Here is the test
#Test
fun medicalInfoJsonContainsFlags(){
val adapter = moshi.adapter(Medicalnfo::class.java)
val jsonStr = adapter.toJson(Medicalnfo(allergies = listOf())) //value is {"allergies":[]}
assert(jsonStr.contains("has_allergies"))
}
If you make hasAllergies as var it will work
data class Medicalnfo(
#Json(name = "allergies") val allergies: List<Allergy>? = null
): ProfileModel(){
#Json(name = "has_allergies") var hasAllergies: Boolean = allergies != null && allergies.isNotEmpty()
}