I have next class structure:
class ExampleResponse(
#SerializedName("status")
val status: String
)
I am creating instance of this class via reflection:
fun convert(typeAdapterFactory: TypeAdapterFactory, gson: Gson): Optional<*> {
return try {
val genericTypeClassName = "com.example.package.ExampleResponse"
val genericClass = Class.forName(genericTypeClassName)
val genericTypeAdapter = gson.getDelegateAdapter(typeAdapterFactory, TypeToken.get(genericClass))
val response = genericTypeAdapter.fromJson("{}")
Optional.of(response)
} catch (e: Exception) {
Optional.empty<Any>()
}
}
I am waiting that on the line genericTypeAdapter.fromJson("{}") it will throw exception, because status will be null. But I am receiving instance of ExampleResponse with null status field.
I want to return Optional.empty() if status is null. How can I achieve this without checking fields? (checking field to non null is not acceptable because the real function is universal and I won't know what class I'll receive here).
Gson doesn't know when a field can/cannot be null, since it's a Java library and Java knows nothing about nullability.
This question and answers suggests some workarounds:
Gson optional and required fields
Related
we received a crash on Firebase for a kotlin method:
Fatal Exception: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter code
at [redacted].DeliveryMethod.<init>(:2)
at [redacted].DeliveryMethodsUpdater$addSingleDMInAd$clientCall$1.invokeSuspend(DeliveryMethodsUpdater.kt:121)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
the model is this one:
class DeliveryMethod() {
lateinit var code: String
lateinit var name: String
lateinit var description: String
var isAddressRequired: Boolean? = null
var image: JSONObject? = null
var isDefault: Boolean = false
constructor(code: String) : this() {
this.code = code
}
constructor(code: String, name: String, description: String, image: JSONObject? = null) : this() {
this.code = code
this.name = name
this.description = description
this.image = image
}
}
and the method:
private suspend fun addSingleDMInAd(
adId: Long,
deliveryMethodCode: String
): JoinAdDeliveryMethod? {
var addedDeliveryMethod: JoinAdDeliveryMethod? = null
val clientCall = GlobalScope.async(Dispatchers.IO) {
val cd = CountDownLatch(1)
Client.getInstance().addDeliveryMethodInAd(
adId,
DeliveryMethod(deliveryMethodCode),
object : NetworkCallback<JoinAdDeliveryMethod> {
override fun onSuccess(result: JoinAdDeliveryMethod) {
addedDeliveryMethod = result
cd.countDown()
}
override fun onFailure(err: NetworkError?) {
addedDeliveryMethod = null
cd.countDown()
}
}
)
cd.await()
}
clientCall.await()
return addedDeliveryMethod
}
now, I understand that that the constructor for DeliveryMethod is being called with a null value for code, but I don't understand why the exception only come up at this point. As you can see, the method param is also marked as non-null, and so are previous methods. Shouldn't the exception be thrown way before getting to the constructor call for DeliveryMethod?
EDIT:
this is the caller of addSingleDMinAd():
fun addDeliveryMethodsInAd(
adId: Long,
deliveryMethodCodesToAdd: List<String>,
completionListener: (List<JoinAdDeliveryMethod?>) -> Unit
) {
GlobalScope.launch {
val updatedDms: MutableList<JoinAdDeliveryMethod?> = mutableListOf()
for (deliveryCode in deliveryMethodCodesToAdd) {
addSingleDMInAd(adId = adId, deliveryMethodCode = deliveryCode).run {
updatedDms.add(this)
}
}
completionListener.invoke(updatedDms)
}
}
this is the java caller of the addDeliveryMethodsInAd() (this is inside an Android Service):
new DeliveryMethodsUpdater().addDeliveryMethodsInAd(
result.getId(),
deliveryMethodCodesToAdd,
updatedDMs -> {
// on failed delivery method request
for (JoinAdDeliveryMethod updatedDm : updatedDMs) {
if (updatedDm == null) {
//show error
break;
}
}
AdDetailUpdater
.getInstance()
.updateSubscribersWithDeliveryMethods(result.getId(), updatedDMs);
return null;
}
);
Shouldn't the exception be thrown way before getting to the constructor call for DeliveryMethod?
Within Kotlin, it's not possible for a non-null parameter to be given a null value at runtime accidentally (because the code wouldn't have compiled in the first place). However, this can happen if the value is passed from Java. This is why the Kotlin compiler tries to protect you from Java's null unsafety by generating null-checks at the beginning of some methods (with the intrinsic checkNotNullParameter you're seeing fail here).
However, there is no point in doing that in private or suspend methods since they can only be called from Kotlin (usually), and it would add some overhead that might not be acceptable in performance-sensitive code. That is why these checks are only generated for non-suspend public/protected/internal methods (because their goal is to prevent misuse from Java).
This is why, if you manage to call addSingleDMInAd with a null argument, it doesn't fail with this error. That said, it would be interesting to see how you're getting the null here, because usually the checks at the public API surface are enough. Is some reflection or unsafe cast involved here?
EDIT: with the addition of the calling code, this clears up the problem. You're calling a method that takes a List<String> from Java, with a list that contains nulls. Unfortunately Kotlin only checks the parameters themselves (in this case, it checks that the list itself is not null), it doesn't iterate your list to check for nulls inside. This is why it didn't fail at the public API surface in this case.
Also, the way your model is setup is quite strange. It seems the lateinit is lying because depending on which constructor is used, the properties may actually not be set at all. It would be safer to mark them as nullable to account for when users of that class don't set the value of these properties. Doing this, you won't even need all secondary constructors, and you can just use default values:
class DeliveryMethod() {
var code: String? = null,
var name: String? = null,
var description: String? = null,
var image: JSONObject? = null,
) {
var isAddressRequired: Boolean? = null
var isDefault: Boolean = false
}
Other things of note about addSingleDMInAd:
don't use GlobalScope in this case. If you need to run short-lived coroutines, provide them with a smaller scope that is cancelled when the work is not needed anymore - it ensures no coroutines are leaked. You can read more about the potential pitfalls of GlobalScope and possible replacements in its own doc. That said, you probably shouldn't be starting a coroutine at all anyway here, see next point.
don't use async {} if you use await() right after - it's pointless to start something asynchronous if you wait for it right there. If you want to switch the context to IO, use withContext(Dispatchers.IO) { ... } instead. That said, you shouldn't even need to use the IO dispatcher here, see next point.
don't use CountDownLatch for this purpose. The proper way to encapsulate an async API as a suspend function for coroutines is to use suspendCancellableCoroutine (check its doc, it provides an example on how to use it). Once you use this, there is no need anymore for Dispatchers.IO because it will not be blocking the current thread anymore.
I have a data https://gist.githubusercontent.com/iva-nova-e-katerina/fc1067e971c71a73a0b525a21b336694/raw/954477261bb5ac2f52cee07a8bc45a2a27de1a8c/data2.json a List with seven CheckResultItem elements.
I trying to parse them this way:
import com.fasterxml.jackson.module.kotlin.readValue
...
val res = restHelper.objectMapper.readValue<List<CheckResultItem>>(text)
which gives me the following error:
com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.fmetric.validation.api.Brick] value failed for JSON property upperLevelBricks due to missing (therefore NULL) value for creator parameter upperLevelBricks which is a non-nullable type
at [Source: (StringReader); line: 1, column: 714] (through reference chain: java.util.ArrayList[0]->com.fmetric.validation.api.checking.CheckResultItem["brick"]->com.fmetric.validation.api.Brick["upperLevelBricks"])
at com.fasterxml.jackson.module.kotlin.KotlinValueInstantiator.createFromObjectWith(KotlinValueInstantiator.kt:116)
There is #JsonIgnore annotation in data class :
data class Brick(
val id: UUID?,
val name: String,
val type: BrickType,
val propertyValues: List<ProjectBrickPropertyValue<*>>,
#JsonIgnore
val upperLevelBricks: ArrayList<Brick>,
val downLevelBricks: ArrayList<Brick>,
var drawingDetails: List<BrickDrawingDetails>?
) {
But it seems it doesn't work. Could you explain me what is wrong?
UPD: Also I have tried #JsonIgnoreProperties({"upperLevelBricks"}) class annotation but it doesn't work. My solution was to set a default value
val upperLevelBricks: ArrayList<Brick> = arrayListOf(),
But I think that annotations should work!
Actually, it works, but not the way you think. During deserialization #JsonIgnore ignores the respectful field in JSON, like it wasn't there (but it's doesn't make sense in this case, because it's initially absent in JSON).
In Java, Jackson would've just instantiated class with null value for the absent field (because all object types in Java are nullable, which means they allow the value to be set to null). But in Kotlin, a property should be explicitly marked as nullable (val upperLevelBricks: List<Brick>?) or have a default value (val upperLevelBricks: List<Brick> = emptyList()) so that Jackson could create a class instance in this case.
Note that approach with default value for property won't work (unless you additionally mark it with #JsonIgnore) if this field is present in JSON but explicitly set to null:
{
...
"upperLevelBricks": null,
...
}
Anyway, if you don't want to change the API of your Brick class you may provide a default value for this field only when it's created during Jackson deserialization (and only if it's absent/null in JSON) via custom deserializer:
object EmptyListAsDefault : JsonDeserializer<List<Brick>>() {
override fun deserialize(jsonParser: JsonParser, context: DeserializationContext): List<Brick> =
jsonParser.codec.readValue(
jsonParser,
context.typeFactory.constructCollectionType(List::class.java, Brick::class.java)
)
override fun getNullValue(context: DeserializationContext): List<Brick> = emptyList()
}
data class Brick(
//...
#JsonDeserialize(using = EmptyListAsDefault::class)
val upperLevelBricks: List<Brick>,
//...
)
I am using Jackson for deserialaizing JSON. We are also using BigDecimal type to deserialize an incoming String into and this value can contain comma's, e.g. ten thousand can be submitted as "10,000". To be able to deserilaize this value into BigDeciaml, I have created a simple custom class:
class BigDecimalCommaDeserializer : JsonDeserializer<BigDecimal>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): BigDecimal? {
return if (parser.text != null && parser.text.isNotEmpty())
BigDecimal(parser.text.replace(",", ""))
else null
}
}
and wired up as follows:
#JsonDeserialize(using = BigDecimalCommaDeserializer::class)
#JsonFormat(shape = JsonFormat.Shape.STRING)
var unitCount: BigDecimal? = null,
I notice though, there is also a com.fasterxml.jackson.databind.deser.std.BigDecimalDeserializer. Will this still be called, or should i think about delegating a call to it from my BigDecimalCommaDeserializer?
Custom deserialiser, BigDecimalCommaDeserializer, will be used in that case. Common one, BigDecimalDeserializer, will be used by default in all other places where custom deserialiser is not specified.
I'm trying to create new rule for detekt project. To do this I have to know exact type of Kotlin property. For example, val x: Int has type Int.
Unfortunately, for property of type private val a = 3 I receive the following:
property.typeReference is null
property.typeParameters is empty
property.typeConstraints is empty
property.typeParameterList is empty
property.text is private val a = 3
property.node.children().joinToString() has object notation of previous item
property.delegate is null
property.getType(bindingContext) is null (the property bindingContext is part of KtTreeVisitorVoid used
Question: how can I get name of type (or, what is better, object KClass) to compare the actual property type with Boolean class type? (e.g. I just need to get if property boolean of not)
Code:
override fun visitProperty(property: org.jetbrains.kotlin.psi.KtProperty) {
val type: String = ??? //property.typeReference?.text - doesn't work
if(property.identifierName().startsWith("is") && type != "Boolean") {
report(CodeSmell(
issue,
Entity.from(property),
message = "Non-boolean properties shouldn't start with 'is' prefix. Actual type: $type")
)
}
}
Right solution:
fun getTypeName(parameter: KtCallableDeclaration): String? {
return parameter.createTypeBindingForReturnType(bindingContext)
?.type
?.getJetTypeFqName(false)
}
There are at least following values for Boolean type: kotlin.Boolean and java.lang.Boolean
Full code is here.
I have below AuthenticationResponse model that I use for 3 api calls using retrofit2. If I make the verifyEmail call f.e. the JSON response body only contains an attribute for valid email (so something like {"validEmail": true} ). The other 2 calls only contain attributes for "resetSuccesful" or the other 4.
How can I make sure/check that when I receive the response to the verifyEmail call f.e. that it contains a non null value for validEmail??
Service:
interface AuthenticationService {
#POST("auth/checkEmail")
fun verifyEmail(#Body email: String): Call<AuthenticationResponse>
#POST("auth/login")
fun login(#Body loginCredentials: LoginCredentials): Call<AuthenticationResponse>
#POST("auth/resetPassword")
fun resetPassword(#Body email: String): Call<AuthenticationResponse>
}
Model:
data class AuthenticationResponse(
val validEmail: Boolean? = null,
val loginSuccess: Boolean? = null,
val retriesLeft: Int? = null,
val authToken: String? = null,
val accountBlocked: Boolean? = null,
val resetSuccesful: Boolean? = null)
edit:
If i mock my server response to return f.e. responseCode = 200 - { "validEmail": null } and change validEmail type to Boolean (instead of Boolean?) Retrofit doesn't thrown any kind of exception (this is what i actually want) thus my model is giving me a false negative for my validEmail value..
You should definitely consider #miensol's comment -- to have separate model objects for different API calls.
However, if that's not possible, you can use Sealed class.
sealed class AuthenticationResponse {
data class EmailValidation(val validEmail: Boolean) : AuthenticationResponse()
data class SomeSecondResponse(val loginSuccess: Boolean, ...) : AuthenticationResponse()
data class SomeThirdResponse(val resetSuccessful: Boolean) : AuthenticationResponse()
}
fun handleResponse(response: AuthenticationResponse) {
when (response) {
is AuthenticationResponse.EmailValidation -> response.validEmail
is AuthenticationResponse.SomeSecondResponse -> response.loginSuccess
is AuthenticationResponse.SomeThirdResponse -> response.resetSuccessful
}
}
Sealed class is enums on steroids -- it is enums with states. You have to create 3 classes for 3 responses which inherit from the sealed class AuthenticationResponse.
You have to create the specific class instance corresponding to the different API calls. To access the data, you can do type check and access the specific data. The above when example shows how to access all the types of response inside a function.
How can I make sure/check that when I receive the response to the
verifyEmail call f.e. that it contains a non null value for
validEmail??
Since you create the instance of only the specific classes and all the classes have only non-null properties, you don't have to worry about null.
I would consider what #miensol mentioned in a comment, but if you wanted to add a check for this, you could do something like:
fun isEmailValid(authResponse: AuthenticationResponse): Boolean {
return authResponse.validEmail ?: false
}
See Kotlin docs on Elvis Operator:
If the expression to the left of ?: is not null, the elvis operator returns it, otherwise it returns the expression to the right. Note that the right-hand side expression is evaluated only if the left-hand side is null.