I've seen many topics about this subject but none of them fit my needs.
I need a way to deserialize null fields into null json properties
Let's take this example:
#JsonClass(generateAdapter = true)
data class MyClass(val myField: String?)
val obj = MyClass(null)
expected behavior:
{
"myField: null
}
By default, it just skips null fields.
I'm working with Retrofit and Moshi.
I don't want to enable withNullSerialization as it will take effect to all Classes (and break the existing logic) and I want this to work for only one Class for now.
Furthermore, for performance and apk size purpose, I removed kotlin-reflect from the project. Which means I would like to avoid using reflection (KotlinJsonAdapterFactory) as many solutions point to that direction.
Is there a way to achieve this ? Thanks in advance.
Looking at the doc, seems you need to write a custom adapter for that specific class:
class MyClassWithNullsAdapter {
#ToJson fun toJson(writer: JsonWriter, myClass: MyClass?,
delegate: JsonAdapter<MyClass?>) {
val wasSerializeNulls: Boolean = writer.getSerializeNulls()
writer.setSerializeNulls(true)
try {
delegate.toJson(writer, myClass)
} finally {
writer.setLenient(wasSerializeNulls)
}
}
}
Related
I'm new to coding in kotlin and want to implement an immutable class that represents a project with various fields inside.
The easiest way to do this is by using a data class and using the copy() method so that anytime one of the app user modifies a field it results in the backend in a call to the copy method with the modified field producing the new project.
My problem is that this way does not allow for prior checking of parameters (eg : limit string size of the owner, making sure the number of people added to the project is reasonable etc).
If this was java, I'd use a builder pattern but this seems to defeat the purpose of kotlin, and i've read articles that are positive to using builders in kotlin (https://www.baeldung.com/kotlin/builder-pattern)
and others that are completely against (https://code-held.com/2021/01/23/dont-use-builder-in-kotlin/).
I haven't found any way to "modify" the copy method and to add the parameter sanitization checks that are needed for each parameter. I would appreciate any "smooth" idea to implement this, if anybody has found it. The goal would also be to throw exeptions/sealed classes variables so that the app UI can tell the user what went wrong instead of a generic error message just mentioning that the project was not modified.
I agree with the second link. If you look at the comments on the Baeldung article, you'll see even they were convinced and pledged to revise the article.
You can throw exceptions in an init block but if these are exceptions that are not caused by programmer error, it would be more Kotlin-idiomatic to expose a single constructor-like function that returns a wrapper or just null for invalid input.
Examples:
data class Person(val name: String, val age: Int = 0) {
init {
if (age < 0) {
throw IllegalArgumentException("Age $age is less than 0.")
}
}
}
If you want to return a wrapper or nullable, a data class isn't suitable for preventing invalid input because the generated copy() function will always return a fully constructed object. Sadly, Kotlin does not support overriding the generated copy() function.
sealed class Result<T>
data class Success<T>(val value: T): Result<T>()
data class Failure<T>(val reason: String): Result<T>()
class Person private constructor(val name: String, val age: Int = 0) {
companion object {
fun build(name: String, age: Int = 0): Result<Person> {
return when {
age < 0 -> Failure("Age $age is less than 0.")
else -> Success(Person(name, age))
}
}
}
fun buildCopy(name: String = this.name, age: Int = this.age) = build(name, age)
}
I'm getting a RARE null pointer exception in the following code:
class Artist {
fun draw(canvas: Canvas, state: State){
state.drawableObjects.forEach {
it.draw(canvas) //NULL POINTER!?! Can not call draw(Canvas) on null object
}
}
}
class State {
var drawableObjects = listOf<DrawableObject>()
set(value) {
field = value.filter { it.isVisible } // Why not crash here if null?
}
}
class DrawableObject(val isVisible: Boolean) {
fun draw(canvas: Canvas) { }
}
How is this possible? The list drawableObjects is immutable. It also does not allow null objects. When the list is changed an entirely new list is set to prevent modification during the draw call.
I should definitely mention that multiple threads are involved. 2 threads only. One calling Artist.draw() and a second calling State.drawableObjects = listOf()
I don't know your data structures, so let's take a simpler case.
Suppose you are trying to populate a List<String> in Kotlin. String is non-nullable, so your Kotlin code will expect that all elements in the List to be not null.
However, it is possible that your List<String> is coming from Java code. In Java, null is perfectly fine to put in a List. And that Java code does not have to be yours — it could be from a library, such as Gson.
So, suppose you have Gson parse this JSON:
[
"the",
"quick",
"brown",
null,
"jumped",
"over",
"the",
"lazy",
null
]
Gson will be happy to parse that into a Kotlin List<String>, and Kotlin's JVM interop will allow it. But, when you start iterating over that List<String>, you will crash with a NullPointerException for the null elements.
Similary, Gson will leave Kotlin properties as null when parsing a JSON object that is missing some of those properties. Suppose you have:
data class Something(foo: Int, bar: Int)
and:
{ foo: 42 }
Gson will happily create a Something instance with bar set to null.
So, my guess, based on your one comment, is that Gson is giving you null values in your List<DrawableObject> based on its parsing rules. You can address this by making that be a List<DrawableObject?> and then doing something about the null elements (filter them out, skip them during your forEach loop, etc.).
I don't believe this is the answer, but I have seen somethign similar in my own project and have gotten around it by using vars and doing the following:
#Suppress("SENSELESS_COMPARISON")
if(myImmutableThatShouldNotBeNull == null){
myImmutableThatShouldNotBeNull = MyClass()
}
Or in your case:
#Suppress("SENSELESS_COMPARISON")
if(canvas == null){
phone.throwOffCliff()!!.now()
}
I have migrated my application from Java to Kotlin. In Java, the copying was working just fine. However, when migrated to Kotline it was not working. After that, I came to know about copy method in Kotlin
I have tied this, but it seems I am doing something wrong.
Here is my function :
fun updateSwitchState(deviceName: String, switchNumber: Int): AuraSwitch? {
val singleDevice = Switch()
for (c in FourNodeDevice) {
if (deviceName == c.name) {
val copyDevice : SwitchClone = SwitchClone(c.state, c.name)
val state = copyDevice.copy(state = copyDevice.state)
state.updateState(switchNumber)
singleDevice.state = state.state
return singleDevice
}
}
return null
}
Whenever I change data in object state in updateState Method the value in object c also gets changed. Any help will be useful
You never create a copy of a state object.
This call creates another SwitchClone with values identical to copyDevice itself.
val state = copyDevice.copy(state = copyDevice.state)
copy() only creates a shallow copy, which means all of your objects, in that case c, copyDevice and state point to the same c.state.
You need to explicitly create a deep copy (depending on what properties are mutable) of state object and assign it to copyDevice.state field.
For Kotlin when using the Kotlin Data Class data class you get a function called copy() for you. But If your Class is not a Data Class and your project has Gson and you want to copy the whole object ( probably edit after getting it ), Then if all those conditions are true then this is a solution. This is also a DeepCopy. ( For a data Class you can use the function copy()).
Then if you are using Gson in your project. Add the function copy():
class YourClass () {
// Your class other stuffs here
fun copy(): YourClass { //Get another instance of YourClass with the values like this!
val json = Gson().toJson(this)
return Gson().fromJson(json, YourClass::class.java)
}
}
If you want to install Gson then get the latest version here.
The copy() did not solve my purpose. However clone() did. I added the following line in my code and it worked as I desired.
val state = c.states.clone()
I am making a list of observable LiveData objects, that should contain Resource object (https://developer.android.com/topic/libraries/architecture/guide.html#addendum). I don't care what type of data that Resource object is containing.
abstract class LiveResources : LiveData<Resource<Any>>() {
private val mediatorLiveData = MediatorLiveData<Resource<Any>>()
protected val resources = HashSet<LiveData<Resource<Any>>>()
fun addResource(source: LiveData<Resource<Any>>) {
resources.add(source)
mediatorLiveData.addSource(source, resourceObserver)
}
fun removeResource(source: LiveData<Resource<Any>>) {
resources.remove(source)
mediatorLiveData.removeSource(source)
}
private val resourceObserver = Observer<Resource<Any>> {
onSourceChange()
}
abstract fun onSourceChange()
}
Unfortunately when I try to use LiveResources.addResource() with LiveData<Resource<List<String>>> I get TypeMismatch error in my IDE, saying that LiveData<Resource<Any>> was expected.
Your Resource (and/or LiveData) class should be defined with generic covariance in order to make it work. Like so:
class Resource<out T> // <- out marks generic type as covariant
Haven't tried it, but I think this would work
fun <T:Any> addResource(source: LiveData<Resource<T>>)
You should generify the classes to accept Resource<T> i.e LiveData<Resource<T>>. Any is the covariance of any object passed, but I think you are not trying to achieve that.
Another friendly advice is that you don't need to add another abstraction on top of MediatorLiveData that solely does the same you have implemented.
I'm trying to figure out if something is possible. Generally, what I'm trying to do is get a class type of a subclass from within the companion object of the superclass ... In the snipped below, treat the __ as what I need
companion object
{
fun fromSnapshot(snapshot: DataSnapshot): __
{
val model = snapshot.getValue(__)
model.key = snapshot.key
// ...
return model
}
}
Some background ... DataSnapshot is from Firebase, and snapshot.getValue() takes a Class<T>. If I were trying to create an instance of, say, a TestModel, code would be as follows
companion object
{
fun fromSnapshot(snapshot: DataSnapshot): TestModel
{
val model = snapshot.getValue(TestModel::java.class)
model.key = snapshot.key
// ...
return model
}
}
I'm not really sure if what I'm asking is possible in Kotlin. I'm pretty sure it isn't in Java. I hate to mention it, but in Swift, this would be accomplished with what I call "big-S self," or Self, which is the class type of instance self. If you don't know Swift, self is equivalent to Java and Kotlin's this.
Any help would be hugely appreciated!
From your code, it seems to be a very generic function. It doesn't matter what T is and in which companion object this function lives, so I have another version:
inline fun <reified T : FirebaseModel> DataSnapshot.toModelOfType() =
getValue(T::class.java).also { it.key = this.key}
It can be used like this:
someSnapshot.toModelOfType<SomeFirebaseModel>()
instead of your
FirebaseModel.fromSnapshot<SomeFirebaseModel>(someSnapshot)
or with imports
fromSnapshot<SomeFirebaseModel>(someSnapshot)
I prefer mine because it's shorter than your version without imports and more fluent than your version with imports.
I personally suggest Prefer extension functions over Java style utility functions.
Even though I sat on this for days without posting a question, I figured it out less than an hour after posting this question. This can be accomplished with a reified generic type, which allows for usage of the generic type from within a function, however these can only be used as inline functions. Here's my solution
companion object
{
inline fun <reified T : FirebaseModel> fromSnapshot(snapshot: DataSnapshot): T
{
val model = snapshot.getValue(T::class.java)
model.key = snapshot.key
return model
}
}