Skip to content

Release 2025-05-04 14:19:09 +0000 #326

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
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
246bd6e
Update kogera version
k163377 Apr 20, 2025
6b96583
Merge pull request #303 from ProjectMapK/k163377-patch-1
k163377 Apr 20, 2025
a7583e8
Fixed to not expose internal function
k163377 Apr 20, 2025
a9ae630
Merge pull request #304 from ProjectMapK/fix/published
k163377 Apr 20, 2025
5c2add0
Remove unnecessary printStackTrace
k163377 Apr 28, 2025
cb8ae91
Merge pull request #306 from ProjectMapK/kotlin970
k163377 Apr 28, 2025
dfe8b7f
Porting test
k163377 Apr 28, 2025
c615b86
Merge pull request #307 from ProjectMapK/kotlin972
k163377 Apr 28, 2025
4061dae
Porting test
k163377 Apr 28, 2025
eb5e292
Merge pull request #308 from ProjectMapK/porting-test
k163377 Apr 28, 2025
d69f6b1
Update mockk
k163377 May 2, 2025
5e03430
Update kotlinter
k163377 May 2, 2025
dd8b7a7
Merge pull request #310 from ProjectMapK/mockk
k163377 May 2, 2025
f438469
Update jackson
k163377 May 2, 2025
5f782a3
Porting tests
k163377 May 2, 2025
e86a449
Merge pull request #312 from ProjectMapK/jackson
k163377 May 3, 2025
3890b71
Added type match check to readValues
k163377 May 2, 2025
186a8a1
Merge pull request #313 from ProjectMapK/296
k163377 May 3, 2025
efde7c7
Fix mockk warn
k163377 May 4, 2025
f9668de
Merge pull request #315 from ProjectMapK/mockk-warn
k163377 May 4, 2025
6498661
Fixed hasRequiredMarker to not override processing by other modules
k163377 May 4, 2025
3e36a65
Merge pull request #316 from ProjectMapK/required-1
k163377 May 4, 2025
e760c59
Added consideration of JsonProperty.isRequired
k163377 May 4, 2025
84cb9e3
Merge pull request #317 from ProjectMapK/required-2
k163377 May 4, 2025
3fb8a29
Fixed to ignore nullToEmpty option in fields and setters
k163377 May 4, 2025
616da22
Fix existing tests
k163377 May 4, 2025
7747184
Porting tests
k163377 May 4, 2025
df0c9b4
Merge pull request #318 from ProjectMapK/required-3
k163377 May 4, 2025
44f9e70
Porting tests
k163377 May 4, 2025
ace591c
Merge pull request #319 from ProjectMapK/required-4
k163377 May 4, 2025
faf6167
Add permission constraint
k163377 May 4, 2025
e571b08
Merge pull request #321 from ProjectMapK/actions2
k163377 May 4, 2025
f6b25b5
Remove unnecessary permission
k163377 May 4, 2025
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
4 changes: 4 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: release
on:
workflow_dispatch:

permissions:
pull-requests: write

jobs:
gitPrRelease:
name: git-pr-release
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ val jacksonVersion = libs.versions.jackson.get()
val generatedSrcPath = "${layout.buildDirectory.get()}/generated/kotlin"

group = groupStr
version = "${jacksonVersion}-beta20"
version = "${jacksonVersion}-beta21"

repositories {
mavenCentral()
Expand Down
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
kotlin = "1.9.25" # Mainly for CI, it can be rewritten by environment variable.
jackson = "2.18.3"
jackson = "2.19.0"

# test libs
junit = "5.12.2"
Expand All @@ -14,10 +14,10 @@ jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations
# test libs
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect" }
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
mockk = "io.mockk:mockk:1.14.0"
mockk = "io.mockk:mockk:1.14.2"
jackson-xml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-xml", version.ref = "jackson" }
jackson-csv = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-csv", version.ref = "jackson" }
jackson-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" }

[plugins]
kotlinter = { id = "org.jmailen.kotlinter", version = "5.0.1" }
kotlinter = { id = "org.jmailen.kotlinter", version = "5.0.2" }
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,8 @@ public fun ObjectMapper.registerKotlinModule(

public inline fun <reified T> jacksonTypeRef(): TypeReference<T> = object : TypeReference<T>() {}

/**
* It is public due to Kotlin restrictions, but should not be used externally.
*/
public inline fun <reified T> Any?.checkTypeMismatch(): T {
@PublishedApi
internal inline fun <reified T> Any?.checkTypeMismatch(): T {
// Basically, this check assumes that T is non-null and the value is null.
// Since this can be caused by both input or ObjectMapper implementation errors,
// a more abstract RuntimeJsonMappingException is thrown.
Expand All @@ -84,10 +82,19 @@ public inline fun <reified T> Any?.checkTypeMismatch(): T {
public inline fun <reified T> ObjectMapper.readValue(jp: JsonParser): T = readValue(jp, jacksonTypeRef<T>())
.checkTypeMismatch()

// TODO: After importing 2.19, import the changes in kotlin-module and uncomment the tests.
public inline fun <reified T> ObjectMapper.readValues(
jp: JsonParser
): MappingIterator<T> = readValues(jp, jacksonTypeRef<T>())
/**
* Shorthand for [ObjectMapper.readValues].
* @throws RuntimeJsonMappingException Especially if [T] is non-null and the value read is null.
* Other cases where the read value is of a different type than [T]
* due to an incorrect customization to [ObjectMapper].
*/
public inline fun <reified T> ObjectMapper.readValues(jp: JsonParser): MappingIterator<T> {
val values = readValues(jp, jacksonTypeRef<T>())

return object : MappingIterator<T>(values) {
override fun nextValue(): T = super.nextValue().checkTypeMismatch()
}
}

/**
* Shorthand for [ObjectMapper.readValue].
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.projectmapk.jackson.module.kogera.annotationIntrospector

import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.annotation.OptBoolean
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.introspect.Annotated
import com.fasterxml.jackson.databind.introspect.AnnotatedField
Expand All @@ -26,29 +28,37 @@ internal class KotlinPrimaryAnnotationIntrospector(
private val nullToEmptyMap: Boolean,
private val cache: ReflectionCache
) : NopAnnotationIntrospector() {
// If a new isRequired is explicitly specified or the old required is true, those values take precedence.
// In other cases, override is done by KotlinModule.
private fun JsonProperty.forceRequiredByAnnotation(): Boolean? = when {
isRequired != OptBoolean.DEFAULT -> isRequired.asBoolean()
required -> true
else -> null
}

// If JsonProperty.required is true, the behavior is clearly specified and the result is paramount.
// Otherwise, the required is determined from the configuration and the definition on Kotlin.
override fun hasRequiredMarker(m: AnnotatedMember): Boolean? {
val byAnnotation = _findAnnotation(m, JSON_PROPERTY_CLASS)
?.let { if (it.required) return true else false }
return cache.getJmClass(m.member.declaringClass)?.let { jmClass ->
// To avoid overwriting processing by other modules, annotations are checked after JmClass has been obtained
_findAnnotation(m, JSON_PROPERTY_CLASS)
?.forceRequiredByAnnotation()
?.let { return it }

return cache.getJmClass(m.member.declaringClass)?.let {
when (m) {
is AnnotatedField -> m.hasRequiredMarker(it)
is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(it)
is AnnotatedParameter -> m.hasRequiredMarker(it)
is AnnotatedField -> m.hasRequiredMarker(jmClass)
is AnnotatedMethod -> m.getRequiredMarkerFromCorrespondingAccessor(jmClass)
is AnnotatedParameter -> m.hasRequiredMarker(jmClass)
else -> null
}
} ?: byAnnotation // If a JsonProperty is available, use it to reduce processing costs.
}
}

// Functions that call this may return incorrect results for value classes whose value type is Collection or Map,
// but this is a rare case and difficult to handle, so it is not supported.
private fun JavaType.hasDefaultEmptyValue() = (nullToEmptyCollection && isCollectionLikeType) ||
(nullToEmptyMap && isMapLikeType)

// The nullToEmpty option also affects serialization,
// but deserialization is preferred because there is currently no way to distinguish between contexts.
private fun AnnotatedField.hasRequiredMarker(jmClass: JmClass): Boolean? {
// Direct access to `AnnotatedField` is only performed if there is no accessor (defined as JvmField),
// so if an accessor is defined, it is ignored.
Expand All @@ -57,7 +67,7 @@ internal class KotlinPrimaryAnnotationIntrospector(
// only a check for the existence of a getter is performed.
// https://youtrack.jetbrains.com/issue/KT-6519
?.let {
if (it.getterName == null) !(it.returnType.isNullable || type.hasDefaultEmptyValue()) else null
if (it.getterName == null) !it.returnType.isNullable else null
}
}

Expand All @@ -68,12 +78,8 @@ internal class KotlinPrimaryAnnotationIntrospector(
): Boolean? = when (parameterCount) {
0 -> jmClass.findPropertyByGetter(member)?.isRequiredByNullability()
1 -> {
if (this.getParameter(0).type.hasDefaultEmptyValue()) {
false
} else {
val memberSignature = member.toSignature()
jmClass.properties.find { it.setterSignature == memberSignature }?.isRequiredByNullability()
}
val memberSignature = member.toSignature()
jmClass.properties.find { it.setterSignature == memberSignature }?.isRequiredByNullability()
}
else -> null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test

private class ArgumentBucketTest {
val mockConverters: List<ValueClassUnboxConverter<Any>?> = mockk {
every { this@mockk[any()] } returns null
}

fun mockValueParameter(mockVararg: Boolean = false, mockOptional: Boolean = false) = mockk<JmValueParameter> {
every { isVararg } returns mockVararg
every { isOptional } returns mockOptional
Expand All @@ -27,7 +23,7 @@ private class ArgumentBucketTest {
val generator = BucketGenerator(
(0..31).map { String::class.java },
(0..31).map { mockValueParameter() },
mockConverters
emptyList()
)
val result = generator.generate()

Expand All @@ -51,7 +47,7 @@ private class ArgumentBucketTest {
Double::class.java
),
(1..8).map { mockValueParameter() },
mockConverters
emptyList()
)
val result = generator.generate()

Expand All @@ -70,7 +66,7 @@ private class ArgumentBucketTest {
val generator = BucketGenerator(
(0..32).map { String::class.java },
(0..32).map { mockValueParameter() },
mockConverters
emptyList()
)
val result = generator.generate()

Expand All @@ -89,7 +85,7 @@ private class ArgumentBucketTest {
val generator = BucketGenerator(
(0..32).map { String::class.java },
(0..32).map { mockValueParameter() },
mockConverters
List(33) { null }
)
val sut = generator.generate()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ class HasRequiredMarkerTest {

assertFalse(desc.isRequired("nullableProp"))
assertFalse(desc.isRequired("nullableField"))
assertFalse(desc.isRequired("collectionProp"))
assertFalse(desc.isRequired("collectionField"))
assertFalse(desc.isRequired("mapProp"))
assertFalse(desc.isRequired("mapField"))
assertTrue(desc.isRequired("collectionProp"))
assertTrue(desc.isRequired("collectionField"))
assertTrue(desc.isRequired("mapProp"))
assertTrue(desc.isRequired("mapField"))
assertTrue(desc.isRequired("nonNullProp"))
assertTrue(desc.isRequired("nonNullField"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ class HasRequiredMarkerTest {
val map: Map<*, *> = emptyMap<Any, Any>()
}

// @see KotlinPrimaryAnnotationIntrospector::AnnotatedField.hasRequiredMarker
@Test
fun failing() {
fun `nullToEmpty does not affect for field`() {
val nullToDefaultMapper = ObjectMapper().registerModule(
KotlinModule.Builder()
.enable(KotlinFeature.NullToEmptyCollection)
Expand All @@ -78,13 +77,7 @@ class HasRequiredMarkerTest {
)
val desc = nullToDefaultMapper.introspectSer<NullToDefaultTarget>()

assertFalse(
desc.isRequired("collection"),
"KotlinPrimaryAnnotationIntrospector::AnnotatedField.hasRequiredMarker fixed"
)
assertFalse(
desc.isRequired("map"),
"KotlinPrimaryAnnotationIntrospector::AnnotatedField.hasRequiredMarker fixed"
)
assertTrue(desc.isRequired("collection"))
assertTrue(desc.isRequired("map"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ReadValueTest {
val src = defaultMapper.createParser("null")
assertThrows<RuntimeJsonMappingException> {
defaultMapper.readValue<String>(src)
}.printStackTrace()
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ class ReadValuesTest {
val itr = mapper.readValues<String>(src)

assertEquals("foo", itr.next())
// TODO: It is expected to be checked after importing 2.19.
// assertThrows<RuntimeJsonMappingException> {
assertDoesNotThrow {
assertThrows<RuntimeJsonMappingException> {
itr.next()
}
}
Expand All @@ -51,9 +49,7 @@ class ReadValuesTest {
val itr = mapper.readValues<String>(src)

assertEquals("foo", itr.nextValue())
// TODO: It is expected to be checked after importing 2.19.
// assertThrows<RuntimeJsonMappingException> {
assertDoesNotThrow {
assertThrows<RuntimeJsonMappingException> {
itr.nextValue()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.github.projectmapk.jackson.module.kogera.zPorted.test.github

import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators
import io.github.projectmapk.jackson.module.kogera.KotlinFeature
import io.github.projectmapk.jackson.module.kogera.jacksonObjectMapper
import io.github.projectmapk.jackson.module.kogera.readValue
import io.github.projectmapk.jackson.module.kogera.testPrettyWriter
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class GitHub281 {
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@type")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator::class, property = "@id")
interface Entity

object NumberEntity : Entity

data class NumberValue(val value: Int) {
val entity = NumberEntity
}

private val json = """
[ {
"value" : 10,
"entity" : {
"@type" : ".GitHub281${'$'}NumberEntity",
"@id" : 1
}
}, {
"value" : 11,
"entity" : 1
} ]
""".trimIndent()

@Test
fun `test writing involving type, id and object`() {
val input = listOf(NumberValue(10), NumberValue(11))

val output = jacksonObjectMapper()
.testPrettyWriter()
.writeValueAsString(input)

assertEquals(json, output)
}

@Test
fun `test reading involving type, id and object`() {
val output = jacksonObjectMapper { disable(KotlinFeature.SingletonSupport) }
.readValue<List<NumberValue>>(json)

assertEquals(2, output.size)
val (a, b) = output
assertEquals(NumberEntity::class.java, a.entity.javaClass)
assertEquals(NumberEntity::class.java, b.entity.javaClass)
assertEquals(10, a.value)
assertEquals(11, b.value)
}
}
Loading