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

CoercionConfig of LogicalType.EmptyString for Boolean types does not respect KotlinModule default settings #400

Closed
jacinpoz opened this issue Dec 2, 2020 · 2 comments
Labels

Comments

@jacinpoz
Copy link

jacinpoz commented Dec 2, 2020

Describe the bug
I have raised this bug in jackson-databind already, but it could require changes here too. In jackson-databind StdDeserializer.java you can find the following Coercion related code:

        final CoercionAction act = _checkFromStringCoercion(ctxt, text,
                LogicalType.Boolean, Boolean.TYPE);
        if (act == CoercionAction.AsNull) {
            _verifyNullForPrimitive(ctxt);
            return false;
        }
        if (act == CoercionAction.AsEmpty) {
            return false;
        }

The problem with automatically assuming the boolean value to be "false" is that KotlinModule has a setting which allows null values to be automatically translated to property default values. See below:

    val kotlinModule = KotlinModule(nullIsSameAsDefault = true)

So if you have a data class with default values such as this:

    enum class LogLevel {
        TRACE, DEBUG, INFO, WARN, ERROR
    }

    data class MapperTest(
        val processName: String,
        val logLevel: LogLevel = LogLevel.WARN,
        val datadump: Boolean = true,
        val expiration: Int = 0
    )

You would expect an empty string for the "datadump" property to be deserialized to the default value "true", but instead it is deserialized to "false" because of the coercion logic shown above.

This works well with the "logLevel" enum property property though. When I pass a null value it is deserialized as the default value, which is LogLevel.WARN.

The coercion settings for ENUM are set like this and work as expected:

            objectMapper.coercionConfigFor(LogicalType.Enum)
                .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull)

The coercion settings for Boolean are set like this and don't work as expected.

            objectMapper.coercionConfigFor(LogicalType.Boolean)
                .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull)

Version information
2.12.0

To Reproduce
You can reproduce this issue by adding the relevant jackson 2.12.0 dependencies (databind and kotlin module) and running the unit test below:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.cfg.CoercionAction
import com.fasterxml.jackson.databind.cfg.CoercionInputShape
import com.fasterxml.jackson.databind.type.LogicalType
import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class BooleanCoercionTest{
    enum class LogLevel {
        TRACE, DEBUG, INFO, WARN, ERROR
    }

    data class MapperTest(
        val processName: String,
        val logLevel: LogLevel = LogLevel.WARN,
        val datadump: Boolean = true,
        val expiration: Int = 0
    )

    @Test
    fun testBooleanCoercion(){
        val objectMapper = ObjectMapper()
        objectMapper.registerModule(KotlinModule(nullIsSameAsDefault = true))
        objectMapper.coercionConfigFor(LogicalType.Enum)
            .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull)
        // Coercion to null/empty Boolean
        objectMapper.coercionConfigFor(LogicalType.Boolean)
            .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull)
        val value = objectMapper.readValue("""{"processName":"NAME", "logLevel":"", "datadump":""}""",MapperTest::class.java)
        assertEquals(LogLevel.WARN, value.logLevel)
        // Expected the value to be "true" but it is "false"
        assertTrue(value.datadump)
    }
}
@jacinpoz jacinpoz added the bug label Dec 2, 2020
@k163377
Copy link
Contributor

k163377 commented Feb 22, 2023

Memo:
This issue appears to be similar to #242.

@k163377
Copy link
Contributor

k163377 commented Oct 15, 2023

The jackson official answer is to enable DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES as the correct way to handle this (see #242 ).
Even if CoercionAction.AsNull is specified, it will eventually be converted to its default value of false by this option (CoercionAction.Fail is processed first, so it has an effect).

At least, this was closed because the behavior was caused by databind and not a kotlin-module issue.

@k163377 k163377 closed this as completed Oct 15, 2023
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

2 participants