Skip to content

Commit fd17846

Browse files
authored
Merge branch '3.x' into tatu-claude/3.1/1419-map-entry-shape-override
2 parents b1b4e0a + 64d892b commit fd17846

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2341
-454
lines changed

.github/workflows/main.yml

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
- "release-notes/*"
1212
permissions:
1313
contents: read
14+
pull-requests: write
1415

1516
jobs:
1617
build:
@@ -60,16 +61,70 @@ jobs:
6061
CI_DEPLOY_PASSWORD: ${{ secrets.CENTRAL_DEPLOY_PASSWORD }}
6162
# MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
6263
run: ./mvnw -B -q -ff -DskipTests -ntp source:jar deploy
63-
- name: Generate code coverage
64-
if: ${{ matrix.release_build && github.event_name != 'pull_request' }}
65-
run: ./mvnw -B -q -ff -ntp test
64+
- name: Generate code coverage
65+
if: ${{ matrix.release_build }}
66+
run: ./mvnw -B -q -ff -ntp test jacoco:report
6667
- name: Publish code coverage
6768
if: ${{ matrix.release_build && github.event_name != 'pull_request' }}
6869
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
6970
with:
7071
token: ${{ secrets.CODECOV_TOKEN }}
7172
files: ./target/site/jacoco/jacoco.xml
7273
flags: unittests
74+
- name: Generate coverage summary
75+
if: ${{ matrix.release_build && github.event_name == 'pull_request' }}
76+
id: jacoco
77+
uses: cicirello/[email protected]
78+
with:
79+
jacoco-csv-file: target/site/jacoco/jacoco.csv
80+
generate-coverage-badge: false
81+
generate-branches-badge: false
82+
generate-summary: true
83+
- name: Add coverage comment to PR
84+
if: ${{ matrix.release_build && github.event_name == 'pull_request' }}
85+
continue-on-error: true
86+
run: |
87+
# Convert decimal to percentage and round to 1 decimal place
88+
COVERAGE=$(awk -v cov="${{ steps.jacoco.outputs.coverage }}" 'BEGIN { printf "%.1f", cov * 100 }')
89+
BRANCHES=$(awk -v br="${{ steps.jacoco.outputs.branches }}" 'BEGIN { printf "%.1f", br * 100 }')
90+
91+
# Determine color for coverage badge using awk (more portable than bc)
92+
COV_COLOR=$(awk -v cov="$COVERAGE" 'BEGIN {
93+
if (cov >= 80) print "brightgreen"
94+
else if (cov >= 60) print "green"
95+
else if (cov >= 40) print "yellow"
96+
else print "red"
97+
}')
98+
99+
BR_COLOR=$(awk -v br="$BRANCHES" 'BEGIN {
100+
if (br >= 80) print "brightgreen"
101+
else if (br >= 60) print "green"
102+
else if (br >= 40) print "yellow"
103+
else print "red"
104+
}')
105+
106+
COMMENT_BODY="## :test_tube: Code Coverage Report
107+
108+
| Metric | Coverage |
109+
|--------|----------|
110+
| **Instructions** | ![coverage](https://img.shields.io/badge/coverage-${COVERAGE}%25-${COV_COLOR}) **${COVERAGE}%** |
111+
| **Branches** | ![branches](https://img.shields.io/badge/branches-${BRANCHES}%25-${BR_COLOR}) **${BRANCHES}%** |
112+
113+
> Coverage data generated from JaCoCo test results
114+
115+
<!-- jacoco-coverage-comment -->"
116+
117+
# Find and delete existing coverage comment
118+
COMMENT_ID=$(gh pr view ${{ github.event.pull_request.number }} --json comments --jq '.comments[] | select(.body | contains("<!-- jacoco-coverage-comment -->")) | .id' | head -1)
119+
120+
if [ -n "$COMMENT_ID" ]; then
121+
gh api -X DELETE "repos/${{ github.repository }}/issues/comments/$COMMENT_ID" || true
122+
fi
123+
124+
# Post new comment (may fail for PRs from forks due to permissions)
125+
gh pr comment ${{ github.event.pull_request.number }} --body "$COMMENT_BODY"
126+
env:
127+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73128

74129
trigger-dep-build-v2:
75130
name: Trigger v2 dep builds

release-notes/CREDITS

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,15 @@ Oliver Drotbohm (@odrotbohm)
127127
* Contributed fix for #4629: `@JsonIncludeProperties` and `@JsonIgnoreProperties`
128128
ignored when deserializing Records
129129
[3.1.0]
130-
130+
* Contributed fix for #5115: `@JsonUnwrapped` Record deserialization can't handle
131+
name collision
132+
[3.1.0]
133+
134+
Viktor Szathmáry (@phraktle)
135+
* Reported #5115: `@JsonUnwrapped` Record deserialization can't handle name collision
136+
(reported by Viktor S)
137+
[3.1.0]
138+
131139
Hélios Gilles (@RoiSoleil)
132140
* Contributed #5413: Add/support forward reference resolution for array values
133141
[3.1.0]

release-notes/VERSION

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Versions: 3.x (for earlier see VERSION-2.x)
1111
#1196: Add opt-in error collection for deserialization
1212
(requested by @odrotbohm)
1313
(contributed by @sri-adarsh-kumar)
14+
#1654: @JsonDeserialize(contentUsing=...) is ignored if content
15+
type is determined by @JsonTypeInfo
16+
(reported by @pdegoeje)
17+
(fix by @cowtowncoder, @JacksonJang)
1418
#1980: Add method `remove(JsonPointer)` in `ContainerNode`
1519
(fix by @cowtowncoder, w/ Claude code)
1620
#3964: Deserialization issue: MismatchedInputException, Bean not
@@ -24,6 +28,12 @@ Versions: 3.x (for earlier see VERSION-2.x)
2428
creator property" when deserializing JSON with dup property to single-property Record
2529
(reported by @sseelmann)
2630
(fix contributed by @JacksonJang)
31+
#5115: `@JsonUnwrapped` Record deserialization can't handle name collision
32+
(reported by Viktor S)
33+
(fix contributed by @JacksonJang)
34+
#5184: `@JsonIgnore` on record method applied to record matching field
35+
at deserialization
36+
(reported by @emouty)
2737
#5350: Add `DeserializationFeature.USE_NULL_FOR_MISSING_REFERENCE_VALUES` for
2838
selecting `null` vs "empty/absent" value when deserializing missing `Optional` value
2939
#5361: Fix Maven SBOM publishing (worked in 3.0.0-rc4 but not in rc5 or later)
@@ -36,6 +46,8 @@ Versions: 3.x (for earlier see VERSION-2.x)
3646
#5456: Additional configuration (`JsonNodeFeature.STRIP_TRAILING_BIGDECIMAL_ZEROES`: true)
3747
to MapperBuilder#configureForJackson2 to closer match Jackson 2 behavior
3848
(contributed by @nrayburn-tech)
49+
#5475: Support `@JsonDeserializeAs` annotation
50+
(implemented by @cowtowncoder, w/ Claude code)
3951

4052
3.0.3 (28-Nov-2025)
4153

release-notes/VERSION-2.x

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Project: jackson-databind
3030
(contributed by Hélios G)
3131
#5429: Formatting and Parsing of Large ISO-8601 Dates is inconsistent
3232
(reported by @DavTurns)
33+
#5475: Support `@JsonDeserializeAs` annotation
34+
(implemented by @cowtowncoder, w/ Claude code)
35+
#5476: Support `@JsonSerializeAs` annotation
36+
(implemented by @cowtowncoder, w/ Claude code)
3337

3438
2.20.2 (not yet released)
3539

src/main/java/tools/jackson/databind/BeanDescription.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,22 @@ public AnnotatedMember findJsonKeyAccessor() {
198198
*/
199199
public abstract AnnotatedMember findJsonValueAccessor();
200200

201+
/**
202+
* Method used to locate the Method or Field of introspected class that
203+
* is annotated with {@link com.fasterxml.jackson.annotation.JsonAnyGetter}
204+
* (or equivalent annotation).
205+
* If no such {@code AnnotatedMember} exists {@code null} is returned.
206+
* If more than one are found, an exception is thrown.
207+
*/
201208
public abstract AnnotatedMember findAnyGetter();
202209

203210
/**
204211
* Method used to locate a mutator (settable field, or 2-argument set method)
205212
* of introspected class that
206-
* implements {@link com.fasterxml.jackson.annotation.JsonAnySetter}.
207-
* If no such mutator exists null is returned. If more than one are found,
208-
* an exception is thrown.
213+
* is annotated with {@link com.fasterxml.jackson.annotation.JsonAnySetter}
214+
* (or equivalent annotation).
215+
* If no such mutator exists {@code null} is returned.
216+
* If more than one are found an exception is thrown.
209217
* Additional checks are also made to see that method signature
210218
* is acceptable: needs to take 2 arguments, first one String or
211219
* Object; second any can be any type.

src/main/java/tools/jackson/databind/DatabindContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
/**
2424
* Shared base class for {@link DeserializationContext} and
2525
* {@link SerializationContext}, context objects passed through data-binding
26-
* process. Designed so that some of implementations can rely on shared
26+
* process. Designed so that some of the implementations can rely on shared
2727
* aspects like access to secondary contextual objects like type factories
2828
* or handler instantiators.
2929
*/

src/main/java/tools/jackson/databind/ObjectMapper.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,7 @@ protected Object readResolve() {
186186
*/
187187

188188
/**
189-
* Factory used for constructing per-call {@link SerializationContext}s.
190-
*<p>
191-
* Note: while serializers are only exposed {@link SerializationContext},
192-
* mappers and readers need to access additional API defined by
193-
* {@link SerializationContextExt}
189+
* Factory used for constructing per-call {@link SerializationContext} instances.
194190
*/
195191
protected final SerializationContexts _serializationContexts;
196192

@@ -207,7 +203,7 @@ protected Object readResolve() {
207203
*/
208204

209205
/**
210-
* Factory used for constructing per-call {@link DeserializationContext}s.
206+
* Factory used for constructing per-call {@link DeserializationContext} instances.
211207
*/
212208
protected final DeserializationContexts _deserializationContexts;
213209

src/main/java/tools/jackson/databind/ObjectWriter.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class ObjectWriter
5454
* Factory used for constructing per-call {@link SerializationContext}s.
5555
*<p>
5656
* Note: while serializers are only exposed {@link SerializationContext},
57-
* mappers and readers need to access additional API defined by
57+
* writers need to access additional API defined by
5858
* {@link SerializationContextExt}
5959
*/
6060
protected final SerializationContexts _serializationContexts;
@@ -80,7 +80,7 @@ public class ObjectWriter
8080
* We may pre-fetch serializer if root type
8181
* is known (has been explicitly declared), and if so, reuse it afterwards.
8282
* This allows avoiding further serializer lookups and increases
83-
* performance a bit on cases where readers are reused.
83+
* performance a bit on cases where writers are reused.
8484
*/
8585
protected final Prefetch _prefetch;
8686

@@ -367,7 +367,7 @@ public ObjectWriter withoutFeatures(FormatFeature... features) {
367367
* as the root type for serialization, instead of runtime dynamic
368368
* type of the root object itself.
369369
*<p>
370-
* Note that method does NOT change state of this reader, but
370+
* Note that the method does NOT change the state of this writer, but
371371
* rather construct and returns a newly configured instance.
372372
*/
373373
public ObjectWriter forType(JavaType rootType) {
@@ -403,7 +403,7 @@ public ObjectWriter forType(TypeReference<?> rootType) {
403403
* use specified date format for serializing dates; or if null passed, one
404404
* that will serialize dates as numeric timestamps.
405405
*<p>
406-
* Note that the method does NOT change state of this reader, but
406+
* Note that the method does NOT change the state of this writer, but
407407
* rather construct and returns a newly configured instance.
408408
*/
409409
public ObjectWriter with(DateFormat df) {
@@ -442,7 +442,7 @@ public ObjectWriter with(PrettyPrinter pp) {
442442
* specifies what root name to use for "root element wrapping".
443443
* See {@link SerializationConfig#withRootName(String)} for details.
444444
*<p>
445-
* Note that method does NOT change state of this reader, but
445+
* Note that the method does NOT change the state of this writer, but
446446
* rather construct and returns a newly configured instance.
447447
*
448448
* @param rootName Root name to use, if non-empty; `null` for "use defaults",
@@ -472,7 +472,7 @@ public ObjectWriter withoutRootName() {
472472
* Method that will construct a new instance that uses specific format schema
473473
* for serialization.
474474
*<p>
475-
* Note that method does NOT change state of this reader, but
475+
* Note that the method does NOT change the state of this writer, but
476476
* rather construct and returns a newly configured instance.
477477
*/
478478
public ObjectWriter with(FormatSchema schema) {
@@ -485,7 +485,7 @@ public ObjectWriter with(FormatSchema schema) {
485485
* serialization view for serialization (with null basically disables
486486
* view processing)
487487
*<p>
488-
* Note that the method does NOT change state of this reader, but
488+
* Note that the method does NOT change the state of this writer, but
489489
* rather construct and returns a newly configured instance.
490490
*/
491491
public ObjectWriter withView(Class<?> view) {
@@ -1279,7 +1279,7 @@ public final static class Prefetch
12791279
* We may pre-fetch serializer if {@link #rootType}
12801280
* is known, and if so, reuse it afterwards.
12811281
* This allows avoiding further serializer lookups and increases
1282-
* performance a bit on cases where readers are reused.
1282+
* performance a bit on cases where writers are reused.
12831283
*/
12841284
private final ValueSerializer<Object> valueSerializer;
12851285

src/main/java/tools/jackson/databind/ValueSerializer.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Set;
55

66
import com.fasterxml.jackson.annotation.JsonFormat;
7+
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
78

89
import tools.jackson.core.*;
910
import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
@@ -252,13 +253,20 @@ public void serializeWithType(T value, JsonGenerator gen, SerializationContext c
252253
TypeSerializer typeSer)
253254
throws JacksonException
254255
{
256+
// 07-Dec-2025, tatu: [databind#1654] Check for "no-op" type serializer
257+
// indirectly
258+
if (typeSer.getTypeInclusion() == As.NOTHING) {
259+
serialize(value, gen, ctxt);
260+
return;
261+
}
262+
255263
Class<?> clz = handledType();
256264
if (clz == null) {
257265
clz = value.getClass();
258266
}
259267
ctxt.reportBadDefinition(clz, String.format(
260-
"Type id handling not implemented for type %s (by serializer of type %s)",
261-
clz.getName(), getClass().getName()));
268+
"Type id handling (method `serializeWithType()`) not implemented for type %s (by serializer of type %s)",
269+
ClassUtil.nameOf(clz), ClassUtil.nameOf(getClass())));
262270
}
263271

264272
/*

src/main/java/tools/jackson/databind/cfg/MapperBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,8 @@ public B withAllCoercionConfigs(Consumer<CoercionConfigs> handler) {
10371037
*/
10381038
public B removeAllModules() {
10391039
_modules = null;
1040+
// [databind#5481]: invalidate cached state when modules are modified
1041+
_savedState = null;
10401042
return _this();
10411043
}
10421044

@@ -1068,6 +1070,8 @@ public B addModule(JacksonModule module)
10681070
_modules.putIfAbsent(dep.getRegistrationId(), dep);
10691071
}
10701072
_modules.put(moduleId, module);
1073+
// [databind#5481]: invalidate cached state when modules are modified
1074+
_savedState = null;
10711075
return _this();
10721076
}
10731077

0 commit comments

Comments
 (0)