Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

There are some problems with KNAI.findDefaultCreator #547

Open
k163377 opened this issue Jan 28, 2022 · 0 comments
Open

There are some problems with KNAI.findDefaultCreator #547

k163377 opened this issue Jan 28, 2022 · 0 comments
Labels

Comments

@k163377
Copy link
Contributor

k163377 commented Jan 28, 2022

I found several processes for KNAI.findDefaultCreator that are not working as intended, or the test results do not change even if they are removed.

1. JsonProperty.Access is ignored in KFunction<*>.isPossibleSingleString.

This function is decraled as follows.

private fun KFunction<*>.isPossibleSingleString(propertyNames: Set<String>): Boolean = parameters.singleOrNull()?.let {
    it.name !in propertyNames
        && it.type.javaType == String::class.java
        && !it.hasAnnotation<JsonProperty>()
} == true

From reading the Javadoc of JsonProperty.Access, it looks like the correct behavior is to ignore it during deserialization if the JsonProperty.access property is READ_ONLY.
https://javadoc.io/static/com.fasterxml.jackson.core/jackson-annotations/1.13.1/com/fasterxml/jackson/annotation/JsonProperty.Access.html

Giving JsonProperty to parameters with READ_ONLY looks like a strange edge case conceptually, but I think it should be fixed if we want to match Javadoc.

2. kConstructor.parameters.any { it.name == null } will not be true

Since KParameter.name is nullable, this decision seems reasonable, but it does not seem to be true in practice.

In the constructor, KParameter.name is null only if get the constructor of inner class as follows.
In this case, outer class is requested as an instance parameter, and its KParameter.name is null.

val anyConstructorHasJsonCreator = kClass.constructors
    .filterOutSingleStringCallables(propertyNames)
    .any { it.hasAnnotation<JsonCreator>() }

val anyCompanionMethodIsJsonCreator = member.type.rawClass.kotlin.companionObject?.declaredFunctions
    ?.filterOutSingleStringCallables(propertyNames)
    ?.any { it.hasAnnotation<JsonCreator>() && it.hasAnnotation<JvmStatic>() }
    ?: false

On the other hand, Jackson does not support deserialization to non-static classes, so this pattern can be ignored.

Also, if KParameter.name is null, it seems to fail with findKotlinParameterName even if this process does not fail.
In this case, the error message will be has no property name instead of no Creators.

I think it would be better to remove this decision, as it eliminates unnecessary processing and appears to make error messages easier to understand.


The content has been rewritten because the modification of #818 has changed the search process for creators.

Original

I found several processes for KNAI.hasCreatorAnnotation that are not working as intended, or the test results do not change even if they are removed.

1. There are some problems with KFunction<*>.isPossibleSingleString.

This function is decraled as follows.

private fun KFunction<*>.isPossibleSingleString(propertyNames: Set<String>): Boolean = parameters.size == 1 &&
        parameters[0].name !in propertyNames &&
        parameters[0].type.javaType == String::class.java &&
        !parameters[0].hasAnnotation<JsonProperty>()

1-1. Unable to get the intended result from functions retrieved with companionObject.declaredFunctions.

The parameters of functions retrieved with companionObject.declaredFunctions include the instance parameter.
In other words, parameters.size == 1 will be true only for functions with no parameters.

Therefore, this decision needs to be modified to valueParameters.size == 1.

1-2.

From reading the Javadoc of JsonProperty.Access, it looks like the correct behavior is to ignore it during deserialization if the JsonProperty.access property is READ_ONLY.
https://javadoc.io/static/com.fasterxml.jackson.core/jackson-annotations/1.13.1/com/fasterxml/jackson/annotation/JsonProperty.Access.html

Giving JsonProperty to parameters with READ_ONLY looks like a strange edge case conceptually, but I think it should be fixed if we want to match Javadoc.

2. Removing kConstructor.isPossibleSingleString does not change the test results.

The process I am referring to here is the following part.
Removing kConstructor.isPossible... from the branch of the when expression does not seem to change the test results.

val propertyNames = kClass.memberProperties.map { it.name }.toSet()

return when {
    kConstructor.isPossibleSingleString(propertyNames) -> false

    /* omission */

}

To begin with, what is the purpose of the isPossibleSingleString decision?
I did not understand why it specifically excludes functions that match this condition.
In addition, I think that this decision should be removed from the KotlinModule process, since there seems to be no bug reports despite the problems mentioned in 1-1.

3. kConstructor.parameters.any { it.name == null } will not be true

Since KParameter.name is nullable, this decision seems reasonable, but it does not seem to be true in practice.

In the constructor, KParameter.name is null only if get the constructor of inner class as follows.
In this case, outer class is requested as an instance parameter, and its KParameter.name is null.

val anyConstructorHasJsonCreator = kClass.constructors
    .filterOutSingleStringCallables(propertyNames)
    .any { it.hasAnnotation<JsonCreator>() }

val anyCompanionMethodIsJsonCreator = member.type.rawClass.kotlin.companionObject?.declaredFunctions
    ?.filterOutSingleStringCallables(propertyNames)
    ?.any { it.hasAnnotation<JsonCreator>() && it.hasAnnotation<JvmStatic>() }
    ?: false

On the other hand, Jackson does not support deserialization to non-static classes, so this pattern can be ignored.

Also, if KParameter.name is null, it seems to fail with findKotlinParameterName even if this process does not fail.
In this case, the error message will be has no property name instead of no Creators.

I think it would be better to remove this decision, as it eliminates unnecessary processing and appears to make error messages easier to understand.

4. JsonCreator.mode is being ignored.

In the following process, all cases where JsonCreator is included are handled in the same way.

val anyConstructorHasJsonCreator = kClass.constructors
    .filterOutSingleStringCallables(propertyNames)
    .any { it.hasAnnotation<JsonCreator>() }

val anyCompanionMethodIsJsonCreator = member.type.rawClass.kotlin.companionObject?.declaredFunctions
    ?.filterOutSingleStringCallables(propertyNames)
    ?.any { it.hasAnnotation<JsonCreator>() && it.hasAnnotation<JvmStatic>() }
    ?: false

On the other hand, if JsonCreator.mode is DISABLED, returning false seems to be correct.

@k163377 k163377 added the bug label Jan 28, 2022
@k163377 k163377 changed the title There are some problems with KNAI.hasCreatorAnnotation There are some problems with KNAI.findDefaultCreator Nov 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant