Skip to content

Coercion shouldn't be necessary for Enums specifying an empty string #4896

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

Closed
1 task done
joaocanaverde-blue opened this issue Jan 10, 2025 · 5 comments
Closed
1 task done
Labels
2.19 Issues planned at 2.19 or later
Milestone

Comments

@joaocanaverde-blue
Copy link

joaocanaverde-blue commented Jan 10, 2025

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

Trying to deserialise:

{ "example": "" }

Results in:

Cannot coerce empty String ("") to `YesOrNoOrEmpty` value (but could if coercion was enabled using `CoercionConfig`)

Despite an empty string ("") being listed as a value in the enum.

enum class YesOrNoOrEmpty(val value: kotlin.String) {
    @JsonProperty(value = "")
    EMPTY(""),

    @JsonProperty(value = "yes")
    YES("yes"),

    @JsonProperty(value = "no")
    NO("no");
}

I'm trying to deserialise historical values, so this is a value I have to support. Ideally I'd still like to deserialise to an enum rather than just into a string, and would like to avoid coercion if possible.

Given that I'm specifying the empty string explicitly with @JsonProperty(value = ""), I'm assuming encountering this exception is a bug rather than intended behaviour?

Version Information

2.18.2

Reproduction

Read:

{ "example": "" }

into an Example object like:

data class Example(
    val example: YesOrNo
)

enum class YesOrNo(val value: kotlin.String) {
    @JsonProperty(value = "")
    EMPTY(""),

    @JsonProperty(value = "yes")
    YES("yes"),

    @JsonProperty(value = "no")
    NO("no");
}

Expected behavior

Deserialisation works.

Additional context

A workaround does exist (as the exception message suggests):

import com.fasterxml.jackson.databind.cfg.CoercionAction
import com.fasterxml.jackson.databind.cfg.CoercionInputShape
import com.fasterxml.jackson.databind.type.LogicalType
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder

@Configuration
class JacksonConfig {
    @Bean
    fun jsonCustomizer(): Jackson2ObjectMapperBuilderCustomizer {
        return Jackson2ObjectMapperBuilderCustomizer { builder: Jackson2ObjectMapperBuilder ->
            builder.postConfigurer { objectMapper ->
                objectMapper.coercionConfigFor(LogicalType.Enum)
                    .setCoercion(CoercionInputShape.EmptyString, CoercionAction.TryConvert)
            }

        }
    }
}
@joaocanaverde-blue joaocanaverde-blue added the to-evaluate Issue that has been received but not yet evaluated label Jan 10, 2025
@cowtowncoder cowtowncoder added 2.18 Issues planned at 2.18 or later and removed to-evaluate Issue that has been received but not yet evaluated labels Jan 12, 2025
@cowtowncoder
Copy link
Member

@joaocanaverde-blue I agree, this sounds like a bug; edge case handled incorrectly.

@cowtowncoder
Copy link
Member

Hmmmh. Interesting... the problem here is actually handling of @JsonProperty("") -- empty String is the default, "unspecified" value. So actual name/id will be "EMPTY" because @JsonProperty is essentially ignored.
Part of the problem is that these two are the same

@JsonProperty("")
@JsonProperty

due to JsonProperty having:

String value() default "";

Will need to think of how to tackle this.

@cowtowncoder
Copy link
Member

Ok: this is fixable, and I will do so.

However, this does change semantics of following case a bit:

public enum MyEnum {
   @JsonProperty // or with ("")
   DEFAULT
}

which would formerly (before fix) use id DEFAULT -- similar to regular POJO properties, where empty/missing value simply means "use whatever default name this property has" (Field name, modified name of Method etc).

Given this, I think I cannot change this in a patch but fix it for 2.19.0 (minor version).
So in case someone somewhere was relying on this behavior (possible accidentally) at least patch won't break that. At the same time, I do not think this is Major-version-only kind of change.

@cowtowncoder cowtowncoder added 2.19 Issues planned at 2.19 or later and removed 2.18 Issues planned at 2.18 or later labels Jan 25, 2025
@cowtowncoder cowtowncoder changed the title Coercion shouldn't be necessary for enums specifying an empty string Coercion shouldn't be necessary for Enums specifying an empty string Jan 25, 2025
@cowtowncoder cowtowncoder modified the milestones: 2.1.7, 2.19.0 Jan 25, 2025
@joaocanaverde-blue
Copy link
Author

Amazing, thanks @cowtowncoder!

@cowtowncoder
Copy link
Member

@joaocanaverde-blue Thank you for your help here, good to have this edge case covered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.19 Issues planned at 2.19 or later
Projects
None yet
Development

No branches or pull requests

2 participants