Moshi Json / Kotlin - Empty String to Null (for numerical properties) - kotlin

I have a model class containing Long and Int properties and I am using Moshi Library to parse a json string into this class.
data class Adjust (
var appId: String?,
var clicks: Long?,
var count: Int?)
If I parse a json like this {"appId":"1", "clicks":""}, I get an error Expected a long but was at path $.clicks
Same thing happens for the Int field.
What can I do short of adding two custom adapters so that the blank strings are parsed as null and do not error out?
The custom adapter I wrote is like this:
object EmptyStringToNullAdapter {
#FromJson
fun fromJson(string: String) = string.toLongOrNull()
#ToJson
fun toJson(value: Long) = value.toString()
}
This works but I have to write another similar one for Int and maybe in future if other numerical fields are added, more such adapters! What is the better approach here?

Related

Define an Enum structure in Kotlin?

I have seen a lot of examples of interfaces for enums methods here but I am looking for another thing.
I want to assure some string enums have at least three keys there:
enum InterstitialEnum(val webName: string) {
Showed("interstitialShowed"),
Dismissed("interstitialDismissed"),
Failed("interstitialFailed"),
SomeInterstititalValue("intersititalSomeValue")
}
enum VideoEnum(val webName: string) {
Showed("videoShowed"),
Dismissed("videoDismissed"),
Failed("videoFailed"),
VideoSomethingHere("videoSomethingHere")
}
My end goal is to use that interface as function parameter, so I can access functionParameter.Showed.webName, etc.
I tried to create an interface but I can not find a way to define Showed, Dismissed or Failed, just functions.
This does not work
interface BaseEnum {
val FailedToShow: String;
}
Edit:
Important, this is not a duplicate of How to extend enums in Kotlin? because I do not want the same key/value pair, I want the same key with different value.
You can’t do this with different enums because there’s no mechanism for relating the names of enum instances of different enums.
Here’s an idea for something that’s similar to the structure you’re looking for.
interface WebNames {
val showed: String
val dismissed: String
val failed: String
}
object InterstitialWebNames: WebNames {
override val showed: String = "interstitialShowed"
override val dismissed: String = "interstitialDismissed"
override val failed: String = "interstitialFailed"
}
object VideoWebNames: WebNames {
override val showed: String = "videoShowed"
override val dismissed: String = "videoDismissed"
override val failed: String = "videoFailed"
}
If you have other properties, you could use a wrapper class instead of Strings for these properties.
I think #TenFour04's example is the closest you're going to get. You can think of an enum as a type, and Showed, Dismissed and Failed as subtypes - but there's no way of enforcing that a particular supertype must have a certain set of subtypes, with specific names.
If you don't just want to deal with String properties (e.g. so you can do something like if (state is Showed) then you could make a type for that:
open class State(val webName: String)
class Showed(webName: String) : State(webName)
class Dismissed(webName: String) : State(webName)
class Failed(webName: String) : State(webName)
interface WebNames {
val showed: Showed
val dismissed: Dismissed
val failed: Failed
}
object VideoWebNames : WebNames {
override val showed = Showed("videoShowed")
override val dismissed = Dismissed("videoDismissed")
override val failed = Failed("videoDismissed")
// a State that's not a standard one included in the interface
val videoSomethingHere = State("videoSomethingHere")
}
if you wanted you could stick all the required states in a sealed class, to group them together and maybe do some checking later
open class State(val webName: String)
sealed class RequiredState(webName: String) : State(webName)
class Showed(webName: String) : RequiredState(webName)
class Dismissed(webName: String) : RequiredState(webName)
class Failed(webName: String) : RequiredState(webName)
So now videoWebNames.showed is a State that also is Showed and is RequiredState

Kotlin multiple class for data storage

I am developing a simple Android app, that will display an icon of a vehicle and the user can click on the icon to display the vehicle information. I want to load the data dynamically when I build the app i.e. the data will come from an external source including the picture for the icon.
I am new to Kotlin and not sure what to search for to understand a suitable solution. What is the correct way to define the data, is it best to create an class as below then create an array of the class (not sure if this is possible)
public class VehicleSpec()
{
var OEM: String? = null
var ModelName: String? = null
var EngineSize: String? = null
}
Or would be better to create a multiple dimension array and then link the data to the cells?
var VehicleSpec = arrayOf(20,20)
VehicleSpec[0][0] = Null //OEM
VehicleSpec[0][1] = Null //ModelName
VehicleSpec[0][2] = Null //EngineSize
What is the best way to set up the data storage, is there any good references to understand how this should be setup?
What is the correct way to define the data, is it best to create an class as below then create an array of the class
Using an array for the properties of an object is not making the full use of the type safety you have in Kotlin (and even Java for that matter).
If what you want to express is multiple properties of an object, then you should use a class to define those properties. This is especially true if the properties have different types.
There is no performance difference between an array and a class, because you'll get a reference to the heap in both cases. You could save on performance only if you convert your multi-dimensional array approach to a single-dimension array with smart indexing. Most of the time, you should not consider this option unless you are handling a lot of data and if you know that performance is an issue at this specific level.
(not sure if this is possible)
Defining lists/arrays of classes is definitely possible.
Usually, for classes that are only used as data containers, you should prefer data classes, because they give you useful methods for free, and these methods totally make sense for simple "data bags" like in your case (equals, hashcode, component access, etc.).
data class Vehicle(
val OEM: String,
val ModelName: String,
val EngineSize: String
)
Also, I suggest using val instead of var as much as possible. Immutability is more idiomatic in Kotlin.
Last but not least, prefer non-null values to null values if you know a value must always be present. If there are valid cases where the value is absent, you should use null instead of a placeholder value like empty string or -1.
First at all, using the "class aprocah" makes it easy for you to understand and give you the full benefits of the language itself... so dont dry to save data in an array .. let the compiler handle those stuff.
Secondly i suggest you have maybe two types (and use data classes ;-) )
data class VehicleListEntry(
val id: Long,
val name: String
)
and
data class VehicleSpec(
val id: Long,
val oem: String = "",
val modelName: String = "",
val engineSize: String = ""
)
from my perspective try to avoid null values whenever possible.
So if you have strings - which you are display only - use empty strings instead of null.
and now have a Model to store your data
class VehicleModel() {
private val specs: MutableMap<Long, VehicleSpec> = mutableMapOf()
private var entries: List<VehicleListEntry> = listOf()
fun getSpec(id: Long) = specs[id]
fun addSpec(spec: VehicleSpec) = specs[spec.id] = spec
fun getEntries(): List<VehicleListEntry> = entries
fun setEntries(data: List<VehicleListEntry>) {
entries = data.toMutableList()
}
}
You could also use a data class for your model which looks like
data class VehicleModel(
val specs: MutableMap<Long, VehicleSpec> = mutableMapOf(),
var entries: List<VehicleListEntry> = listOf()
)
And last but not least a controller for getting stuff together
class VehicleController() {
private val model = VehicleModel()
init{
// TODO get the entries list together
}
fun getEntries() = model.entries
fun getSpec(id: Long) : VehicleSpec? {
// TODO load the data from external source (or check the model first)
// TODO store the data into the model
// TODO return result
}
}

How parse raw json list of data posted in ktor

I'm posting json array of objects. I'm trying to parse it in code like this
val objs = call.receive<List<MyClass>>() // this work fine
val name objs[0].name // this throw exception LinkedTreeMap cannot be cast to MyClass
In above code second line throws exception com.google.gson.internal.LinkedTreeMap cannot be cast to MyClass
If i post simple object and parse it in ktor with call.receive<MyClass>() then it will work fine. So issue is only when parsing list of objects.
Using your code with Array instead of List worked for me using ktor v1.2.3:
val objs = call.receive<Array<MyClass>>()
val name = objs[0].name
Side Note:
I later changed my data class to this format to help with mapping from database rows to a data class (i.e. to use BeanPropertyRowMapper). I don't remember this having an effect on de/serialization, but if the first part still isn't working for you, you may try this...
data class MyClass(
var id: Int? = null,
var name: String? = null,
var description: String? = null,
)
Reference: Kotlin data class optional variable
You can do like this
val json = call.receive<String>()
val objs = Gson().fromJson(json, Array<MyClass>::class.java)
objs[0].name
Updated
You can also create extention function for that like this
suspend inline fun <reified T> ApplicationCall.safeReceive(): T {
val json = this.receiveOrNull<String>()
return Gson().fromJson(json, T::class.java)
}
then use it like this
val objs = call.safeReceive<Array<MyClass>>()
objs[0].name

ArrayList<AbstractObject> adding objects which extended AbstractObject is not possible. How to fix it?

So I have ArrayList<AbstractObject> which is class type abstract. And I have 2 items which extends AbstractObject. If I use abstractList.add(Object1) it says that ArrayList expects object of type AbstractObject and not Object1. I thought that this is possible. Reason why I want to do this is to use multiple objects with 2 different data in single RecyclerView. (ViewTypes)
abstract class ListItem {
abstract val type: Int
companion object {
const val TYPE_HEADER = 0
const val TYPE_ITEM = 1
}
}
class HeaderItem(val headerTitle: String) : ListItem() {
val type: Int
get() = TYPE_HEADER
}
class ObjectItem(val object: ParseObject) : ListItem() {
val type: Int
get() = TYPE_ITEM
}
Init #1:
var recyclerViewArray: ArrayList<out ListItem> = ArrayList()
This is error if I want to add HeaderItem to this list:
Init #2:
var recyclerViewArray: ArrayList<ListItem> = ArrayList()
This says Type mismatch. Tried with as but as is yellowed with message This cast can never succeed.
It is possible and it works fine. If in some expression Kotlin infers the type wrong, you can always specify it manually. In your case
abstractList.add(Object1 as AbstractObject)
Remove out from the declaration of recyclerViewArray and it should work (I just tried it and it ran fine). e.g. I could run this line of code:
recyclerViewArray.add(HeaderItem("test"))
Note that when you remove out you still need to keep the fact that it's an ArrayList of ListItem objects. So you should declare it as:
var recyclerViewArray = arrayListOf<ListItem>()
Some of your code didn't quite compile for me, like having a property called object (I had to put backticks around that) and not putting the override modifier on the type property on HeaderItem.

Jackson deserialization - Kotlin data classes - Defaults for missing fields per mapper

Given this data class:
data class MyPojo(val notInJson: Int, val inJson: Int)
Assume I want to implement a function of the form:
fun deserialize(jsonString: String, valueForFieldNotInJson: Int): MyPojo
Where jsonString does not include a field named notInJson. Assume also, that I have no control over MyPojo class definition.
How could I use Jackson library to deserialize MyPojo from jsonString and augment the missing field (notInJson) from valueForFieldNotInJson parameter?
Notes:
Basically, the question is about deserializing a Immutable class, where some fields come from Json and others are supplied at runtime.
Using custom deserializers or builders will not work because missing values are unknow at compile time.
This can be achieved by combining MinInAnnotations and ValueInjection.
Complete solution as follows:
data class MyPojo(val notInJson: Int, val inJson: Int)
class MyPojoMixIn {
#JacksonInject("notInJson") val notInJson: Int = 0
}
fun deserialize(jsonString: String, valueForFieldNotInJson: Int): MyPojo {
val injectables = InjectableValues.Std().addValue("notInJson", valueForFieldNotInJson)
val reader = jacksonObjectMapper()
.addMixIn(MyPojo::class.java, MyPojoMixIn::class.java)
.readerFor(MyPojo::class.java)
.with(injectables)
return reader.readValue(jsonString)
}