diff --git a/pom.xml b/pom.xml
index e43b8115a1..6c92882610 100644
--- a/pom.xml
+++ b/pom.xml
@@ -394,6 +394,59 @@
+
+
+ java21
+
+ 21
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-test-source
+ generate-test-sources
+
+ add-test-source
+
+
+
+ src/test-jdk14/java
+ src/test-jdk17/java
+ src/test-jdk21/java
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ true
+
+
+ 21
+ 21
+
+ -parameters
+ --add-opens=java.base/java.lang=ALL-UNNAMED
+ --add-opens=java.base/java.util=ALL-UNNAMED
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED
+
+
+
+
+
errorprone
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
index 575afa32d5..85d1066946 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -2562,6 +2562,10 @@ protected static class ContainerDefaultMappings {
fallbacks.put(Deque.class.getName(), LinkedList.class);
fallbacks.put(NavigableSet.class.getName(), TreeSet.class);
+ // Sequenced types added in JDK21
+ fallbacks.put("java.util.SequencedCollection", DEFAULT_LIST);
+ fallbacks.put("java.util.SequencedSet", LinkedHashSet.class);
+
_collectionFallbacks = fallbacks;
}
@@ -2582,6 +2586,9 @@ protected static class ContainerDefaultMappings {
fallbacks.put(java.util.concurrent.ConcurrentNavigableMap.class.getName(),
java.util.concurrent.ConcurrentSkipListMap.class);
+ // Sequenced types added in JDK21
+ fallbacks.put("java.util.SequencedMap", LinkedHashMap.class);
+
_mapFallbacks = fallbacks;
}
diff --git a/src/test-jdk21/java/com/fasterxml/jackson/databind/jdk21/Java21CollectionsTest.java b/src/test-jdk21/java/com/fasterxml/jackson/databind/jdk21/Java21CollectionsTest.java
new file mode 100644
index 0000000000..4f2908ef1a
--- /dev/null
+++ b/src/test-jdk21/java/com/fasterxml/jackson/databind/jdk21/Java21CollectionsTest.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.databind.jdk21;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+
+public class Java21CollectionsTest extends BaseMapTest
+{
+ // [databind#4089]
+ record SequencedCollections(
+ SequencedCollection sequencedCollection,
+ SequencedSet sequencedSet,
+ SequencedMap sequencedMap) {
+ }
+
+ public void testSequencedCollectionTypesDeserialize() throws Exception {
+ String json = """
+ {
+ "sequencedCollection": ["A", "B"],
+ "sequencedSet": ["C", "D"],
+ "sequencedMap": {"A": 1, "B": 2}
+ }
+ """;
+
+ ObjectMapper objectMapper = JsonMapper.builder().build();
+ SequencedCollections value = objectMapper.readValue(json, SequencedCollections.class);
+ assertEquals(ArrayList.class, value.sequencedCollection.getClass());
+ assertEquals(LinkedHashSet.class, value.sequencedSet.getClass());
+ assertEquals(LinkedHashMap.class, value.sequencedMap.getClass());
+ }
+
+ public void testSequencedCollectionTypesRoundTrip() throws Exception {
+ ArrayList arrayList = new ArrayList<>();
+ arrayList.add("A");
+ arrayList.add("B");
+ LinkedHashSet linkedHashSet = new LinkedHashSet<>();
+ linkedHashSet.add("C");
+ linkedHashSet.add("D");
+ LinkedHashMap linkedHashMap = new LinkedHashMap<>();
+ linkedHashMap.put("A", 1);
+ linkedHashMap.put("B", 2);
+ SequencedCollections input = new SequencedCollections(arrayList, linkedHashSet, linkedHashMap);
+
+ ObjectMapper objectMapper = JsonMapper.builder().build();
+ String json = objectMapper.writeValueAsString(input);
+ SequencedCollections value = objectMapper.readValue(json, SequencedCollections.class);
+ assertEquals(input, value);
+ }
+}