Skip to content

Commit bf56d81

Browse files
authored
Merge branch '3.x' into tatu/3.0/887-use-module-info
2 parents 67b3256 + ed68f66 commit bf56d81

25 files changed

+395
-116
lines changed

.github/workflows/dep_build_v2.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ jobs:
1414
strategy:
1515
fail-fast: false
1616
matrix:
17-
java_version: ['8', '17', '21', '23']
17+
java_version: ['8', '17', '21', '24']
1818
# Versions need to align with ones in 'main.yml' workflow
19-
kotlin_version: ['1.9.24', '2.0.21', '2.1.0']
19+
kotlin_version: ['1.9.24', '2.0.21', '2.1.20']
2020
env:
2121
JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
2222
steps:

.github/workflows/dep_build_v3.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ jobs:
1414
strategy:
1515
fail-fast: false
1616
matrix:
17-
java_version: ['17', '21', '23']
17+
java_version: ['17', '21', '24']
1818
# Versions need to align with ones in 'main.yml' workflow
19-
kotlin_version: ['1.9.24', '2.0.21', '2.1.0']
19+
kotlin_version: ['1.9.24', '2.0.21', '2.1.20']
2020
env:
2121
JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
2222
steps:

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ jobs:
2525
fail-fast: false
2626
max-parallel: 5
2727
matrix:
28-
java_version: ['17', '21', '23']
29-
kotlin_version: ['1.9.25', '2.0.21', '2.1.0']
28+
java_version: ['17', '21', '24']
29+
kotlin_version: ['1.9.25', '2.0.21', '2.1.20']
3030
include:
3131
- java_version: '17'
3232
kotlin_version: '1.9.25'

pom.xml

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
32
<!-- This module was also published with a richer model, Gradle metadata, -->
43
<!-- which should be used instead. Do not delete the following line which -->
54
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
@@ -9,12 +8,12 @@
98
<parent>
109
<groupId>tools.jackson</groupId>
1110
<artifactId>jackson-base</artifactId>
12-
<version>3.0.0-SNAPSHOT</version>
11+
<version>3.0.0-rc3-SNAPSHOT</version>
1312
</parent>
1413
<groupId>tools.jackson.module</groupId>
1514
<artifactId>jackson-module-kotlin</artifactId>
1615
<name>jackson-module-kotlin</name>
17-
<version>3.0.0-SNAPSHOT</version>
16+
<version>3.0.0-rc3-SNAPSHOT</version>
1817
<packaging>jar</packaging>
1918
<description>Add-on module for Jackson (https://github.com/FasterXML/jackson/) to support
2019
Kotlin language, specifically introspection of method/constructor parameter names,
@@ -55,12 +54,6 @@
5554
</scm>
5655

5756
<properties>
58-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
59-
60-
<!-- as per [databind#4820], minimum of Java 17 for Jackson 3.0 -->
61-
<javac.src.version>17</javac.src.version>
62-
<javac.target.version>17</javac.target.version>
63-
6457
<version.kotlin>1.9.25</version.kotlin>
6558

6659
<!-- Generate PackageVersion.java into this directory. -->
@@ -84,7 +77,6 @@
8477
<groupId>tools.jackson.core</groupId>
8578
<artifactId>jackson-databind</artifactId>
8679
</dependency>
87-
8880
<dependency>
8981
<groupId>com.fasterxml.jackson.core</groupId>
9082
<artifactId>jackson-annotations</artifactId>
@@ -127,12 +119,16 @@
127119
<artifactId>jackson-dataformat-xml</artifactId>
128120
<scope>test</scope>
129121
</dependency>
122+
<!-- 06-Apr-2025, tatu: JSR-310 module merged in jackson-databind
123+
for 3.0.0-rc3
124+
-->
125+
<!--
130126
<dependency>
131-
<!-- needed for kotlin.time.Duration converter test -->
132127
<groupId>tools.jackson.datatype</groupId>
133128
<artifactId>jackson-datatype-jsr310</artifactId>
134129
<scope>test</scope>
135130
</dependency>
131+
-->
136132
</dependencies>
137133

138134
<build>
@@ -178,12 +174,6 @@
178174
<!-- Inherited from oss-base. Generate PackageVersion.java.-->
179175
<groupId>com.google.code.maven-replacer-plugin</groupId>
180176
<artifactId>replacer</artifactId>
181-
<executions>
182-
<execution>
183-
<id>process-packageVersion</id>
184-
<phase>generate-sources</phase>
185-
</execution>
186-
</executions>
187177
</plugin>
188178
<plugin>
189179
<groupId>org.apache.maven.plugins</groupId>
@@ -197,7 +187,7 @@
197187

198188
<!-- 05-Jul-2020, tatu: Add generation of Gradle Module Metadata -->
199189
<plugin>
200-
<groupId>de.jjohannes</groupId>
190+
<groupId>org.gradlex</groupId>
201191
<artifactId>gradle-module-metadata-maven-plugin</artifactId>
202192
</plugin>
203193

@@ -210,7 +200,7 @@
210200
<dependency>
211201
<groupId>tools.jackson.module</groupId>
212202
<artifactId>jackson-module-kotlin</artifactId>
213-
<version>3.0.0-SNAPSHOT</version>
203+
<version>3.0.0-rc1-SNAPSHOT</version>
214204
<type>jar</type>
215205
</dependency>
216206
</oldVersion>

release-notes/CREDITS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ Authors:
1717

1818
Contributors:
1919

20+
# 3.0.0-rc2 (not yet released)
21+
22+
WrongWrong (@k163377)
23+
* #936: Change of default settings for 3.0
24+
25+
# 3.0.0-rc1 (07-Mar-2025)
26+
2027
hokita
2128
* #702: Fix outdated link in master branch README
2229

release-notes/CREDITS-2.x

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ Contributors:
1717

1818
# 2.19.0 (not yet released)
1919

20+
Tatu Saloranta (@cowtowncoder)
21+
* #889: Upgrade kotlin dep to 1.9.25 (from 1.9.24)
22+
2023
WrongWrong (@k163377)
24+
* #930: Add tests for #917
25+
* #929: Bug fixes to hasRequiredMarker and added isRequired considerations
2126
* #914: Add test case to serialize Nothing? (for #314)
2227
* #910: Add default KeyDeserializer for value class
2328
* #885: Performance improvement of strictNullChecks
@@ -30,7 +35,13 @@ WrongWrong (@k163377)
3035
* #839: Remove useKotlinPropertyNameForGetter and unify with kotlinPropertyNameAsImplicitName
3136
* #835: Remove old SingletonSupport class and unified with KotlinFeature.SingletonSupport
3237

33-
# 2.18.3 (not yet released)
38+
# 2.18.4 (not yet released)
39+
40+
WrongWrong (@k163377)
41+
* #923: Fixed hasRequiredMarker to only process content defined in Kotlin
42+
* #920: Minor refactors that do not affect behavior
43+
44+
# 2.18.3 (28-Feb-2025)
3445

3546
WrongWrong (@k163377)
3647
* #908: Additional fixes related to #904.

release-notes/VERSION

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ Former maintainers:
2222
=== Releases ===
2323
------------------------------------------------------------------------
2424

25-
3.0.0 (not yet released)
25+
3.0.0-rc2 (28-Mar-2025)
26+
27+
#936: `StrictNullChecks` and `SingletonSupport` are now enabled by default
28+
29+
3.0.0-rc1 (07-Mar-2025)
2630

2731
#887: Change 3.0 to use `module-info.java` directly [JSTEP-11]
2832
- Minimum Java baseline: Java 17

release-notes/VERSION-2.x

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ Co-maintainers:
1818

1919
2.19.0 (not yet released)
2020

21+
#929: Added consideration of `JsonProperty.isRequired` added in `2.19` in `hasRequiredMarker` processing.
22+
Previously `JsonProperty.required` was defined as `Boolean` with default `false`,
23+
so `KotlinModule` was forced to override it if the value was `false`.
24+
This made it impossible for users to override the parsed result by `KotlinModule`.
25+
The new `JsonProperty.isRequired` is defined with three values, including the default,
26+
so `KotlinModule` can now respect user specifications.
27+
#929: Fixed a problem with the `NullToEmptyCollection` and `NullToEmptyMap` options overriding annotated specifications
28+
in the `hasRequiredMarker` process.
29+
#929: Fixed a problem with the `NullToEmptyCollection` and `NullToEmptyMap` options being applied to non-parameters
30+
in the `hasRequiredMarker` process.
31+
They currently do not work for setters or fields and are not related to serialization,
32+
but were being incorrectly applied to their `required` decisions.
2133
#910: A default `KeyDeserializer` for `value class` has been added.
2234
This eliminates the need to have a custom `KeyDeserializer` for each `value class` when using it as a key in a `Map`, if only simple boxing is needed.
2335
#889: Kotlin has been upgraded to 1.9.25.
@@ -36,7 +48,13 @@ Co-maintainers:
3648
#839: Remove useKotlinPropertyNameForGetter and unify with kotlinPropertyNameAsImplicitName.
3749
#835: Remove old SingletonSupport class and unified with KotlinFeature.SingletonSupport.
3850

39-
2.18.3 (not yet released)
51+
2.18.4 (not yet released)
52+
53+
#923: Fixed a problem where the result of processing `hasRequiredMarker ` by a `KotlinModule` would also apply to
54+
classes defined in `Java` when `NullToEmptyCollection` or `NullToEmptyMap` was enabled.
55+
#920: Minor refactorings were made that did not affect behavior.
56+
57+
2.18.3 (28-Feb-2025)
4058

4159
#904: Fixed a problem where context was not being propagated properly when serializing an unboxed value of `value class`
4260
or a value retrieved with `JsonValue`.

src/main/kotlin/tools/jackson/module/kotlin/KotlinAnnotationIntrospector.kt

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tools.jackson.module.kotlin
22

33
import com.fasterxml.jackson.annotation.JsonProperty
4+
import com.fasterxml.jackson.annotation.OptBoolean
45
import tools.jackson.databind.DeserializationFeature
56
import tools.jackson.databind.JacksonModule
67
import tools.jackson.databind.cfg.MapperConfig
@@ -42,23 +43,33 @@ internal class KotlinAnnotationIntrospector(
4243
// TODO: implement nullIsSameAsDefault flag, which represents when TRUE that if something has a default value, it can be passed a null to default it
4344
// this likely impacts this class to be accurate about what COULD be considered required
4445

45-
override fun hasRequiredMarker(cfg : MapperConfig<*>, m: AnnotatedMember): Boolean? =
46+
// If a new isRequired is explicitly specified or the old required is true, those values take precedence.
47+
// In other cases, override is done by KotlinModule.
48+
private fun JsonProperty.forceRequiredByAnnotation(): Boolean? = when {
49+
isRequired != OptBoolean.DEFAULT -> isRequired.asBoolean()
50+
required -> true
51+
else -> null
52+
}
53+
54+
private fun AccessibleObject.forceRequiredByAnnotation(): Boolean? =
55+
getAnnotation(JsonProperty::class.java)?.forceRequiredByAnnotation()
56+
57+
override fun hasRequiredMarker(
58+
cfg : MapperConfig<*>,
59+
m: AnnotatedMember
60+
): Boolean? = m.takeIf { it.member.declaringClass.isKotlinClass() }?.let { _ ->
4661
cache.javaMemberIsRequired(m) {
4762
try {
48-
when {
49-
nullToEmptyCollection && m.type.isCollectionLikeType -> false
50-
nullToEmptyMap && m.type.isMapLikeType -> false
51-
m.member.declaringClass.isKotlinClass() -> when (m) {
52-
is AnnotatedField -> m.hasRequiredMarker()
53-
is AnnotatedMethod -> m.hasRequiredMarker()
54-
is AnnotatedParameter -> m.hasRequiredMarker()
55-
else -> null
56-
}
63+
when (m) {
64+
is AnnotatedField -> m.hasRequiredMarker()
65+
is AnnotatedMethod -> m.hasRequiredMarker()
66+
is AnnotatedParameter -> m.hasRequiredMarker()
5767
else -> null
5868
}
59-
} catch (ex: UnsupportedOperationException) {
69+
} catch (_: UnsupportedOperationException) {
6070
null
6171
}
72+
}
6273
}
6374

6475
override fun findSerializationConverter(config: MapperConfig<*>?, a: Annotated): Converter<*, *>? = when (a) {
@@ -104,28 +115,9 @@ internal class KotlinAnnotationIntrospector(
104115
}
105116

106117
private fun AnnotatedField.hasRequiredMarker(): Boolean? {
107-
val byAnnotation = (member as Field).isRequiredByAnnotation()
108-
val byNullability = (member as Field).kotlinProperty?.returnType?.isRequired()
109-
110-
return requiredAnnotationOrNullability(byAnnotation, byNullability)
111-
}
112-
113-
private fun AccessibleObject.isRequiredByAnnotation(): Boolean? = annotations
114-
?.firstOrNull { it.annotationClass == JsonProperty::class }
115-
?.let { it as JsonProperty }
116-
?.required
117-
118-
private fun requiredAnnotationOrNullability(byAnnotation: Boolean?, byNullability: Boolean?): Boolean? {
119-
if (byAnnotation != null && byNullability != null) {
120-
return byAnnotation || byNullability
121-
} else if (byNullability != null) {
122-
return byNullability
123-
}
124-
return byAnnotation
125-
}
126-
127-
private fun Method.isRequiredByAnnotation(): Boolean? {
128-
return (this.annotations.firstOrNull { it.annotationClass.java == JsonProperty::class.java } as? JsonProperty)?.required
118+
val field = member as Field
119+
return field.forceRequiredByAnnotation()
120+
?: field.kotlinProperty?.returnType?.isRequired()
129121
}
130122

131123
// Since Kotlin's property has the same Type for each field, getter, and setter,
@@ -140,33 +132,35 @@ internal class KotlinAnnotationIntrospector(
140132
private fun AnnotatedMethod.getRequiredMarkerFromCorrespondingAccessor(): Boolean? {
141133
member.declaringClass.kotlin.declaredMemberProperties.forEach { kProperty ->
142134
if (kProperty.javaGetter == this.member || (kProperty as? KMutableProperty1)?.javaSetter == this.member) {
143-
val byAnnotation = this.member.isRequiredByAnnotation()
144-
val byNullability = kProperty.isRequiredByNullability()
145-
return requiredAnnotationOrNullability(byAnnotation, byNullability)
135+
return member.forceRequiredByAnnotation() ?: kProperty.isRequiredByNullability()
146136
}
147137
}
148138
return null
149139
}
150140

151141
// Is the member method a regular method of the data class or
152142
private fun Method.getRequiredMarkerFromAccessorLikeMethod(): Boolean? = cache.kotlinFromJava(this)?.let { func ->
153-
val byAnnotation = this.isRequiredByAnnotation()
154-
return when {
155-
func.isGetterLike() -> requiredAnnotationOrNullability(byAnnotation, func.returnType.isRequired())
156-
func.isSetterLike() -> requiredAnnotationOrNullability(byAnnotation, func.valueParameters[0].isRequired())
143+
forceRequiredByAnnotation() ?: when {
144+
func.isGetterLike() -> func.returnType.isRequired()
145+
// If nullToEmpty could be supported for setters,
146+
// a branch similar to AnnotatedParameter.hasRequiredMarker should be added.
147+
func.isSetterLike() -> func.valueParameters[0].isRequired()
157148
else -> null
158149
}
159150
}
160151

161152
private fun KFunction<*>.isGetterLike(): Boolean = parameters.size == 1
162153
private fun KFunction<*>.isSetterLike(): Boolean = parameters.size == 2 && returnType == UNIT_TYPE
163154

164-
private fun AnnotatedParameter.hasRequiredMarker(): Boolean? {
165-
val byAnnotation = this.getAnnotation(JsonProperty::class.java)?.required
166-
val byNullability = cache.findKotlinParameter(this)?.isRequired()
167-
168-
return requiredAnnotationOrNullability(byAnnotation, byNullability)
169-
}
155+
private fun AnnotatedParameter.hasRequiredMarker(): Boolean? = getAnnotation(JsonProperty::class.java)
156+
?.forceRequiredByAnnotation()
157+
?: run {
158+
when {
159+
nullToEmptyCollection && type.isCollectionLikeType -> false
160+
nullToEmptyMap && type.isMapLikeType -> false
161+
else -> cache.findKotlinParameter(this)?.isRequired()
162+
}
163+
}
170164

171165
private fun AnnotatedMethod.findValueClassReturnType() = cache.findValueClassReturnType(this)
172166

src/main/kotlin/tools/jackson/module/kotlin/KotlinFeature.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@ enum class KotlinFeature(internal val enabledByDefault: Boolean) {
2525
NullIsSameAsDefault(enabledByDefault = false),
2626

2727
/**
28-
* By default, there's no special handling of singletons (pre-2.10 behavior).
29-
* Each time a Singleton object is deserialized a new instance is created.
30-
*
31-
* When this feature is enabled, it will deserialize then canonicalize (was the default in 2.10).
28+
* When this feature is enabled, it will deserialize then canonicalize.
3229
* Deserializing a singleton overwrites the value of the single instance.
3330
*
31+
* The 2.x default was disabled, and a new instance was created each time a singleton object was deserialized.
32+
*
3433
* See [jackson-module-kotlin#225]: keep Kotlin singletons as singletons.
3534
*/
36-
SingletonSupport(enabledByDefault = false),
35+
SingletonSupport(enabledByDefault = true),
3736

3837
/**
3938
* This feature represents whether to check deserialized collections.
@@ -88,8 +87,10 @@ enum class KotlinFeature(internal val enabledByDefault: Boolean) {
8887
* This is a temporary option for a phased backend migration,
8988
* which will eventually be merged into [StrictNullChecks].
9089
* Also, specifying both this and [StrictNullChecks] is not permitted.
90+
*
91+
* Since 3.0, this option is enabled by default.
9192
*/
92-
NewStrictNullChecks(enabledByDefault = false);
93+
NewStrictNullChecks(enabledByDefault = true);
9394

9495
internal val bitSet: BitSet = (1 shl ordinal).toBitSet()
9596

0 commit comments

Comments
 (0)