Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ import kotlinx.serialization.encoding.Encoder
*/
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun encode(data: kotlin.Any?): kotlin.String? = if (data is {{classname}}) "$data" else null

{{^jackson}}
/**
* Returns a valid [{{classname}}] for [data], null otherwise.
*/
{{^jackson}}
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}? = data?.let {
val normalizedData = "$it".lowercase()
entries.firstOrNull { value ->
Expand All @@ -120,6 +120,12 @@ import kotlinx.serialization.encoding.Encoder
}
{{/jackson}}
{{#jackson}}
/**
* Returns a valid [{{classname}}] for [data].{{^isNullable}}{{^enumUnknownDefaultCase}}
*
* Throws [IllegalArgumentException] when [data] is null or does not match a known value.
* For lenient lookup that returns null on unknown values, see [decodeOrNull].{{/enumUnknownDefaultCase}}{{/isNullable}}
*/
@JvmStatic
@JsonCreator
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decode(data: kotlin.Any?): {{classname}}{{#isNullable}}?{{/isNullable}} {
Expand All @@ -138,6 +144,20 @@ import kotlinx.serialization.encoding.Encoder
?: {{#allowableValues}}{{#enumVars}}{{#-last}}{{&name}}{{/-last}}{{/enumVars}}{{/allowableValues}}{{/enumUnknownDefaultCase}}{{^enumUnknownDefaultCase}}
?: throw IllegalArgumentException("Unknown {{classname}} value: $data"){{/enumUnknownDefaultCase}}{{/isNullable}}
}

/**
* Returns a valid [{{classname}}] for [data], or null when [data] is null or does not match a known value.
*
* Lenient counterpart to [decode], intended for direct calls from Kotlin code.
* Jackson deserialization uses [decode], which is strict.
*/
@JvmStatic
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}fun decodeOrNull(data: kotlin.Any?): {{classname}}? = data?.let {
val normalizedData = "$it".lowercase()
entries.firstOrNull { value ->
it == value || normalizedData == "$value".lowercase()
}
}
{{/jackson}}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,39 @@ public void testJacksonEnumsThrowForUnknownValue() throws IOException {
TestUtils.assertFileContains(enumKt, "throw IllegalArgumentException(\"Unknown ExampleNumericEnum value: $data\")");
}

@Test
public void testJacksonEnumsExposeDecodeOrNullHelper() throws IOException {
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("kotlin")
.setLibrary("jvm-retrofit2")
.setAdditionalProperties(new HashMap<>() {{
put(CodegenConstants.SERIALIZATION_LIBRARY, "jackson");
put(CodegenConstants.MODEL_PACKAGE, "model");
}})
.setInputSpec("src/test/resources/3_0/kotlin/issue22534-kotlin-numeric-enum.yaml")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));

final ClientOptInput clientOptInput = configurator.toClientOptInput();
DefaultGenerator generator = new DefaultGenerator();

generator.opts(clientOptInput).generate();

final Path enumKt = Paths.get(output + "/src/main/kotlin/model/ExampleNumericEnum.kt");

// decodeOrNull should always be generated alongside decode in the Jackson branch
// and must return null for unknown values without throwing.
TestUtils.assertFileContains(enumKt, "fun decodeOrNull(data: kotlin.Any?): ExampleNumericEnum?");
// decodeOrNull should not be annotated with @JsonCreator — only decode is the Jackson entry point.
// Verify by checking @JsonCreator appears exactly once in the file.
String content = new String(java.nio.file.Files.readAllBytes(enumKt), java.nio.charset.StandardCharsets.UTF_8);
int jsonCreatorCount = content.split("@JsonCreator", -1).length - 1;
Assert.assertEquals(jsonCreatorCount, 1,
"Expected exactly one @JsonCreator annotation in the generated enum, found " + jsonCreatorCount);
}

@Test
public void testJacksonEnumsWithUnknownDefaultCase() throws IOException {
File output = Files.createTempDirectory("test").toFile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ enum class StringEnumRef(@get:JsonValue val value: kotlin.String) {
fun encode(data: kotlin.Any?): kotlin.String? = if (data is StringEnumRef) "$data" else null

/**
* Returns a valid [StringEnumRef] for [data], null otherwise.
* Returns a valid [StringEnumRef] for [data].
*/
@JvmStatic
@JsonCreator
Expand All @@ -80,6 +80,20 @@ enum class StringEnumRef(@get:JsonValue val value: kotlin.String) {
}
?: unknown_default_open_api
}

/**
* Returns a valid [StringEnumRef] for [data], or null when [data] is null or does not match a known value.
*
* Lenient counterpart to [decode], intended for direct calls from Kotlin code.
* Jackson deserialization uses [decode], which is strict.
*/
@JvmStatic
fun decodeOrNull(data: kotlin.Any?): StringEnumRef? = data?.let {
val normalizedData = "$it".lowercase()
entries.firstOrNull { value ->
it == value || normalizedData == "$value".lowercase()
}
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ enum class StringEnumRef(@get:JsonValue val value: kotlin.String) {
fun encode(data: kotlin.Any?): kotlin.String? = if (data is StringEnumRef) "$data" else null

/**
* Returns a valid [StringEnumRef] for [data], null otherwise.
* Returns a valid [StringEnumRef] for [data].
*/
@JvmStatic
@JsonCreator
Expand All @@ -80,6 +80,20 @@ enum class StringEnumRef(@get:JsonValue val value: kotlin.String) {
}
?: unknown_default_open_api
}

/**
* Returns a valid [StringEnumRef] for [data], or null when [data] is null or does not match a known value.
*
* Lenient counterpart to [decode], intended for direct calls from Kotlin code.
* Jackson deserialization uses [decode], which is strict.
*/
@JvmStatic
fun decodeOrNull(data: kotlin.Any?): StringEnumRef? = data?.let {
val normalizedData = "$it".lowercase()
entries.firstOrNull { value ->
it == value || normalizedData == "$value".lowercase()
}
}
}
}

Loading